video: fbdev: pmag-aa-fb: Adapt to current APIs
Rework the driver to use the current frambuffer and TURBOchannel APIs, including proper resource management and using the new framework for hardware cursor support. NB two Bt431 cursor generators are included onboard, both responding at the same TURBOchannel bus addresses and with their host data buses wired to byte lanes #0 and #1 respectively of the 32-bit bus. Therefore both can be accessed simultaneously with 16-bit data transfers. Cursor outputs of the chip wired to lane #0 drive the respective overlay select inputs of the Bt455 RAMDAC, whereas cursor outputs of the chip wired to lane #1 drive the respective P3 pixel select inputs of the RAMDAC. So 5 (out of 17) Bt455 color registers are usable with this board: palette entries #0 and #1 for frame buffer pixel data driven while neither cursor generator is active, palette entries #8 and #9 for frame buffer pixel data driven while cursor generator #1 is active only and the overlay entry while cursor generator #0 is active. Signed-off-by: Maciej W. Rozycki <macro@linux-mips.org> Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>
This commit is contained in:
parent
af22f647b4
commit
90c83176e5
@ -2,6 +2,7 @@
|
||||
* linux/drivers/video/bt431.h
|
||||
*
|
||||
* Copyright 2003 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
|
||||
* Copyright 2016 Maciej W. Rozycki <macro@linux-mips.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
|
||||
@ -9,6 +10,8 @@
|
||||
*/
|
||||
#include <linux/types.h>
|
||||
|
||||
#define BT431_CURSOR_SIZE 64
|
||||
|
||||
/*
|
||||
* Bt431 cursor generator registers, 32-bit aligned.
|
||||
* Two twin Bt431 are used on the DECstation's PMAG-AA.
|
||||
@ -196,27 +199,29 @@ static inline void bt431_position_cursor(struct bt431_regs *regs, u16 x, u16 y)
|
||||
bt431_write_reg_inc(regs, (y >> 8) & 0x0f); /* BT431_REG_CYHI */
|
||||
}
|
||||
|
||||
static inline void bt431_set_font(struct bt431_regs *regs, u8 fgc,
|
||||
u16 width, u16 height)
|
||||
static inline void bt431_set_cursor(struct bt431_regs *regs,
|
||||
const char *data, const char *mask,
|
||||
u16 rop, u16 width, u16 height)
|
||||
{
|
||||
u16 x, y;
|
||||
int i;
|
||||
u16 fgp = fgc ? 0xffff : 0x0000;
|
||||
u16 bgp = fgc ? 0x0000 : 0xffff;
|
||||
|
||||
i = 0;
|
||||
width = DIV_ROUND_UP(width, 8);
|
||||
bt431_select_reg(regs, BT431_REG_CRAM_BASE);
|
||||
for (i = BT431_REG_CRAM_BASE; i <= BT431_REG_CRAM_END; i++) {
|
||||
u16 value;
|
||||
for (y = 0; y < BT431_CURSOR_SIZE; y++)
|
||||
for (x = 0; x < BT431_CURSOR_SIZE / 8; x++) {
|
||||
u16 val = 0;
|
||||
|
||||
if (height << 6 <= i << 3)
|
||||
value = bgp;
|
||||
else if (width <= i % 8 << 3)
|
||||
value = bgp;
|
||||
else if (((width >> 3) & 0xffff) > i % 8)
|
||||
value = fgp;
|
||||
if (y < height && x < width) {
|
||||
val = mask[i];
|
||||
if (rop == ROP_XOR)
|
||||
val = (val << 8) | (val ^ data[i]);
|
||||
else
|
||||
value = fgp & ~(bgp << (width % 8 << 1));
|
||||
|
||||
bt431_write_cmap_inc(regs, value);
|
||||
val = (val << 8) | (val & data[i]);
|
||||
i++;
|
||||
}
|
||||
bt431_write_cmap_inc(regs, val);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
* and Harald Koerfgen <hkoerfg@web.de>, which itself is derived from
|
||||
* "HP300 Topcat framebuffer support (derived from macfb of all things)
|
||||
* Phil Blundell <philb@gnu.org> 1998"
|
||||
* Copyright (c) 2016 Maciej W. Rozycki
|
||||
*
|
||||
* 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
|
||||
@ -21,37 +22,29 @@
|
||||
*
|
||||
* 2003-09-21 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
|
||||
* Hardware cursor support.
|
||||
*
|
||||
* 2016-02-21 Maciej W. Rozycki <macro@linux-mips.org>
|
||||
* Version 0.03: Rewritten for the new FB and TC APIs.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/kernel.h>
|
||||
|
||||
#include <linux/compiler.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/timer.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/fb.h>
|
||||
#include <linux/console.h>
|
||||
|
||||
#include <asm/bootinfo.h>
|
||||
#include <asm/dec/machtype.h>
|
||||
#include <asm/dec/tc.h>
|
||||
|
||||
#include <video/fbcon.h>
|
||||
#include <video/fbcon-cfb8.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/tc.h>
|
||||
#include <linux/timer.h>
|
||||
|
||||
#include "bt455.h"
|
||||
#include "bt431.h"
|
||||
|
||||
/* Version information */
|
||||
#define DRIVER_VERSION "0.02"
|
||||
#define DRIVER_VERSION "0.03"
|
||||
#define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>"
|
||||
#define DRIVER_DESCRIPTION "PMAG-AA Framebuffer Driver"
|
||||
|
||||
/* Prototypes */
|
||||
static int aafb_set_var(struct fb_var_screeninfo *var, int con,
|
||||
struct fb_info *info);
|
||||
|
||||
/*
|
||||
* Bt455 RAM DAC register base offset (rel. to TC slot base address).
|
||||
*/
|
||||
@ -68,443 +61,239 @@ static int aafb_set_var(struct fb_var_screeninfo *var, int con,
|
||||
*/
|
||||
#define PMAG_AA_ONBOARD_FBMEM_OFFSET 0x200000
|
||||
|
||||
struct aafb_cursor {
|
||||
struct timer_list timer;
|
||||
int enable;
|
||||
int on;
|
||||
int vbl_cnt;
|
||||
int blink_rate;
|
||||
u16 x, y, width, height;
|
||||
struct aafb_par {
|
||||
void __iomem *mmio;
|
||||
struct bt455_regs __iomem *bt455;
|
||||
struct bt431_regs __iomem *bt431;
|
||||
};
|
||||
|
||||
#define CURSOR_TIMER_FREQ (HZ / 50)
|
||||
#define CURSOR_BLINK_RATE (20)
|
||||
#define CURSOR_DRAW_DELAY (2)
|
||||
|
||||
struct aafb_info {
|
||||
struct fb_info info;
|
||||
struct display disp;
|
||||
struct aafb_cursor cursor;
|
||||
struct bt455_regs *bt455;
|
||||
struct bt431_regs *bt431;
|
||||
unsigned long fb_start;
|
||||
unsigned long fb_size;
|
||||
unsigned long fb_line_length;
|
||||
static struct fb_var_screeninfo aafb_defined = {
|
||||
.xres = 1280,
|
||||
.yres = 1024,
|
||||
.xres_virtual = 2048,
|
||||
.yres_virtual = 1024,
|
||||
.bits_per_pixel = 8,
|
||||
.grayscale = 1,
|
||||
.red.length = 0,
|
||||
.green.length = 1,
|
||||
.blue.length = 0,
|
||||
.activate = FB_ACTIVATE_NOW,
|
||||
.accel_flags = FB_ACCEL_NONE,
|
||||
.sync = FB_SYNC_ON_GREEN,
|
||||
.vmode = FB_VMODE_NONINTERLACED,
|
||||
};
|
||||
|
||||
/*
|
||||
* Max 3 TURBOchannel slots -> max 3 PMAG-AA.
|
||||
*/
|
||||
static struct aafb_info my_fb_info[3];
|
||||
static struct fb_fix_screeninfo aafb_fix = {
|
||||
.id = "PMAG-AA",
|
||||
.smem_len = (2048 * 1024),
|
||||
.type = FB_TYPE_PACKED_PIXELS,
|
||||
.visual = FB_VISUAL_MONO10,
|
||||
.ypanstep = 1,
|
||||
.ywrapstep = 1,
|
||||
.line_length = 2048,
|
||||
.mmio_len = PMAG_AA_ONBOARD_FBMEM_OFFSET - PMAG_AA_BT455_OFFSET,
|
||||
};
|
||||
|
||||
static struct aafb_par {
|
||||
} current_par;
|
||||
|
||||
static int currcon = -1;
|
||||
|
||||
static void aafb_set_cursor(struct aafb_info *info, int on)
|
||||
static int aafb_cursor(struct fb_info *info, struct fb_cursor *cursor)
|
||||
{
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
struct aafb_par *par = info->par;
|
||||
|
||||
if (on) {
|
||||
bt431_position_cursor(info->bt431, c->x, c->y);
|
||||
bt431_enable_cursor(info->bt431);
|
||||
} else
|
||||
bt431_erase_cursor(info->bt431);
|
||||
}
|
||||
|
||||
static void aafbcon_cursor(struct display *disp, int mode, int x, int y)
|
||||
{
|
||||
struct aafb_info *info = (struct aafb_info *)disp->fb_info;
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
|
||||
x *= fontwidth(disp);
|
||||
y *= fontheight(disp);
|
||||
|
||||
if (c->x == x && c->y == y && (mode == CM_ERASE) == !c->enable)
|
||||
return;
|
||||
|
||||
c->enable = 0;
|
||||
if (c->on)
|
||||
aafb_set_cursor(info, 0);
|
||||
c->x = x - disp->var.xoffset;
|
||||
c->y = y - disp->var.yoffset;
|
||||
|
||||
switch (mode) {
|
||||
case CM_ERASE:
|
||||
c->on = 0;
|
||||
break;
|
||||
case CM_DRAW:
|
||||
case CM_MOVE:
|
||||
if (c->on)
|
||||
aafb_set_cursor(info, c->on);
|
||||
else
|
||||
c->vbl_cnt = CURSOR_DRAW_DELAY;
|
||||
c->enable = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static int aafbcon_set_font(struct display *disp, int width, int height)
|
||||
{
|
||||
struct aafb_info *info = (struct aafb_info *)disp->fb_info;
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
u8 fgc = ~attr_bgcol_ec(disp, disp->conp, &info->info);
|
||||
|
||||
if (width > 64 || height > 64 || width < 0 || height < 0)
|
||||
if (cursor->image.height > BT431_CURSOR_SIZE ||
|
||||
cursor->image.width > BT431_CURSOR_SIZE) {
|
||||
bt431_erase_cursor(par->bt431);
|
||||
return -EINVAL;
|
||||
|
||||
c->height = height;
|
||||
c->width = width;
|
||||
|
||||
bt431_set_font(info->bt431, fgc, width, height);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void aafb_cursor_timer_handler(unsigned long data)
|
||||
{
|
||||
struct aafb_info *info = (struct aafb_info *)data;
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
|
||||
if (!c->enable)
|
||||
goto out;
|
||||
|
||||
if (c->vbl_cnt && --c->vbl_cnt == 0) {
|
||||
c->on ^= 1;
|
||||
aafb_set_cursor(info, c->on);
|
||||
c->vbl_cnt = c->blink_rate;
|
||||
}
|
||||
|
||||
out:
|
||||
c->timer.expires = jiffies + CURSOR_TIMER_FREQ;
|
||||
add_timer(&c->timer);
|
||||
}
|
||||
if (!cursor->enable)
|
||||
bt431_erase_cursor(par->bt431);
|
||||
|
||||
static void __init aafb_cursor_init(struct aafb_info *info)
|
||||
{
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
if (cursor->set & FB_CUR_SETPOS)
|
||||
bt431_position_cursor(par->bt431,
|
||||
cursor->image.dx, cursor->image.dy);
|
||||
if (cursor->set & FB_CUR_SETCMAP) {
|
||||
u8 fg = cursor->image.fg_color ? 0xf : 0x0;
|
||||
u8 bg = cursor->image.bg_color ? 0xf : 0x0;
|
||||
|
||||
c->enable = 1;
|
||||
c->on = 1;
|
||||
c->x = c->y = 0;
|
||||
c->width = c->height = 0;
|
||||
c->vbl_cnt = CURSOR_DRAW_DELAY;
|
||||
c->blink_rate = CURSOR_BLINK_RATE;
|
||||
bt455_write_cmap_entry(par->bt455, 8, 0, bg, 0);
|
||||
bt455_write_cmap_entry(par->bt455, 9, 0, bg, 0);
|
||||
bt455_write_ovly_entry(par->bt455, 0, 0, fg, 0);
|
||||
}
|
||||
if (cursor->set & (FB_CUR_SETSIZE | FB_CUR_SETSHAPE | FB_CUR_SETIMAGE))
|
||||
bt431_set_cursor(par->bt431,
|
||||
cursor->image.data, cursor->mask, cursor->rop,
|
||||
cursor->image.width, cursor->image.height);
|
||||
|
||||
init_timer(&c->timer);
|
||||
c->timer.data = (unsigned long)info;
|
||||
c->timer.function = aafb_cursor_timer_handler;
|
||||
mod_timer(&c->timer, jiffies + CURSOR_TIMER_FREQ);
|
||||
}
|
||||
|
||||
static void __exit aafb_cursor_exit(struct aafb_info *info)
|
||||
{
|
||||
struct aafb_cursor *c = &info->cursor;
|
||||
|
||||
del_timer_sync(&c->timer);
|
||||
}
|
||||
|
||||
static struct display_switch aafb_switch8 = {
|
||||
.setup = fbcon_cfb8_setup,
|
||||
.bmove = fbcon_cfb8_bmove,
|
||||
.clear = fbcon_cfb8_clear,
|
||||
.putc = fbcon_cfb8_putc,
|
||||
.putcs = fbcon_cfb8_putcs,
|
||||
.revc = fbcon_cfb8_revc,
|
||||
.cursor = aafbcon_cursor,
|
||||
.set_font = aafbcon_set_font,
|
||||
.clear_margins = fbcon_cfb8_clear_margins,
|
||||
.fontwidthmask = FONTWIDTH(4)|FONTWIDTH(8)|FONTWIDTH(12)|FONTWIDTH(16)
|
||||
};
|
||||
|
||||
static void aafb_get_par(struct aafb_par *par)
|
||||
{
|
||||
*par = current_par;
|
||||
}
|
||||
|
||||
static int aafb_get_fix(struct fb_fix_screeninfo *fix, int con,
|
||||
struct fb_info *info)
|
||||
{
|
||||
struct aafb_info *ip = (struct aafb_info *)info;
|
||||
|
||||
memset(fix, 0, sizeof(struct fb_fix_screeninfo));
|
||||
strcpy(fix->id, "PMAG-AA");
|
||||
fix->smem_start = ip->fb_start;
|
||||
fix->smem_len = ip->fb_size;
|
||||
fix->type = FB_TYPE_PACKED_PIXELS;
|
||||
fix->ypanstep = 1;
|
||||
fix->ywrapstep = 1;
|
||||
fix->visual = FB_VISUAL_MONO10;
|
||||
fix->line_length = 1280;
|
||||
fix->accel = FB_ACCEL_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aafb_set_disp(struct display *disp, int con,
|
||||
struct aafb_info *info)
|
||||
{
|
||||
struct fb_fix_screeninfo fix;
|
||||
|
||||
disp->fb_info = &info->info;
|
||||
aafb_set_var(&disp->var, con, &info->info);
|
||||
if (disp->conp && disp->conp->vc_sw && disp->conp->vc_sw->con_cursor)
|
||||
disp->conp->vc_sw->con_cursor(disp->conp, CM_ERASE);
|
||||
disp->dispsw = &aafb_switch8;
|
||||
disp->dispsw_data = 0;
|
||||
|
||||
aafb_get_fix(&fix, con, &info->info);
|
||||
disp->screen_base = (u8 *) fix.smem_start;
|
||||
disp->visual = fix.visual;
|
||||
disp->type = fix.type;
|
||||
disp->type_aux = fix.type_aux;
|
||||
disp->ypanstep = fix.ypanstep;
|
||||
disp->ywrapstep = fix.ywrapstep;
|
||||
disp->line_length = fix.line_length;
|
||||
disp->next_line = 2048;
|
||||
disp->can_soft_blank = 1;
|
||||
disp->inverse = 0;
|
||||
disp->scrollmode = SCROLL_YREDRAW;
|
||||
|
||||
aafbcon_set_font(disp, fontwidth(disp), fontheight(disp));
|
||||
}
|
||||
|
||||
static int aafb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
|
||||
struct fb_info *info)
|
||||
{
|
||||
static u16 color[2] = {0x0000, 0x000f};
|
||||
static struct fb_cmap aafb_cmap = {0, 2, color, color, color, NULL};
|
||||
|
||||
fb_copy_cmap(&aafb_cmap, cmap, kspc ? 0 : 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aafb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
|
||||
struct fb_info *info)
|
||||
{
|
||||
u16 color[2] = {0x0000, 0x000f};
|
||||
|
||||
if (cmap->start == 0
|
||||
&& cmap->len == 2
|
||||
&& memcmp(cmap->red, color, sizeof(color)) == 0
|
||||
&& memcmp(cmap->green, color, sizeof(color)) == 0
|
||||
&& memcmp(cmap->blue, color, sizeof(color)) == 0
|
||||
&& cmap->transp == NULL)
|
||||
return 0;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int aafb_ioctl(struct fb_info *info, u32 cmd, unsigned long arg)
|
||||
{
|
||||
/* TODO: Not yet implemented */
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
static int aafb_switch(int con, struct fb_info *info)
|
||||
{
|
||||
struct aafb_info *ip = (struct aafb_info *)info;
|
||||
struct display *old = (currcon < 0) ? &ip->disp : (fb_display + currcon);
|
||||
struct display *new = (con < 0) ? &ip->disp : (fb_display + con);
|
||||
|
||||
if (old->conp && old->conp->vc_sw && old->conp->vc_sw->con_cursor)
|
||||
old->conp->vc_sw->con_cursor(old->conp, CM_ERASE);
|
||||
|
||||
/* Set the current console. */
|
||||
currcon = con;
|
||||
aafb_set_disp(new, con, ip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void aafb_encode_var(struct fb_var_screeninfo *var,
|
||||
struct aafb_par *par)
|
||||
{
|
||||
var->xres = 1280;
|
||||
var->yres = 1024;
|
||||
var->xres_virtual = 2048;
|
||||
var->yres_virtual = 1024;
|
||||
var->xoffset = 0;
|
||||
var->yoffset = 0;
|
||||
var->bits_per_pixel = 8;
|
||||
var->grayscale = 1;
|
||||
var->red.offset = 0;
|
||||
var->red.length = 0;
|
||||
var->red.msb_right = 0;
|
||||
var->green.offset = 0;
|
||||
var->green.length = 1;
|
||||
var->green.msb_right = 0;
|
||||
var->blue.offset = 0;
|
||||
var->blue.length = 0;
|
||||
var->blue.msb_right = 0;
|
||||
var->transp.offset = 0;
|
||||
var->transp.length = 0;
|
||||
var->transp.msb_right = 0;
|
||||
var->nonstd = 0;
|
||||
var->activate &= ~FB_ACTIVATE_MASK & FB_ACTIVATE_NOW;
|
||||
var->accel_flags = 0;
|
||||
var->sync = FB_SYNC_ON_GREEN;
|
||||
var->vmode &= ~FB_VMODE_MASK & FB_VMODE_NONINTERLACED;
|
||||
}
|
||||
|
||||
static int aafb_get_var(struct fb_var_screeninfo *var, int con,
|
||||
struct fb_info *info)
|
||||
{
|
||||
if (con < 0) {
|
||||
struct aafb_par par;
|
||||
|
||||
memset(var, 0, sizeof(struct fb_var_screeninfo));
|
||||
aafb_get_par(&par);
|
||||
aafb_encode_var(var, &par);
|
||||
} else
|
||||
*var = info->var;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aafb_set_var(struct fb_var_screeninfo *var, int con,
|
||||
struct fb_info *info)
|
||||
{
|
||||
struct aafb_par par;
|
||||
|
||||
aafb_get_par(&par);
|
||||
aafb_encode_var(var, &par);
|
||||
info->var = *var;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int aafb_update_var(int con, struct fb_info *info)
|
||||
{
|
||||
struct aafb_info *ip = (struct aafb_info *)info;
|
||||
struct display *disp = (con < 0) ? &ip->disp : (fb_display + con);
|
||||
|
||||
if (con == currcon)
|
||||
aafbcon_cursor(disp, CM_ERASE, ip->cursor.x, ip->cursor.y);
|
||||
if (cursor->enable)
|
||||
bt431_enable_cursor(par->bt431);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 0 unblanks, any other blanks. */
|
||||
|
||||
static void aafb_blank(int blank, struct fb_info *info)
|
||||
static int aafb_blank(int blank, struct fb_info *info)
|
||||
{
|
||||
struct aafb_info *ip = (struct aafb_info *)info;
|
||||
struct aafb_par *par = info->par;
|
||||
u8 val = blank ? 0x00 : 0x0f;
|
||||
|
||||
bt455_write_cmap_entry(ip->bt455, 1, val, val, val);
|
||||
aafbcon_cursor(&ip->disp, CM_ERASE, ip->cursor.x, ip->cursor.y);
|
||||
bt455_write_cmap_entry(par->bt455, 1, val, val, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct fb_ops aafb_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.fb_get_fix = aafb_get_fix,
|
||||
.fb_get_var = aafb_get_var,
|
||||
.fb_set_var = aafb_set_var,
|
||||
.fb_get_cmap = aafb_get_cmap,
|
||||
.fb_set_cmap = aafb_set_cmap,
|
||||
.fb_ioctl = aafb_ioctl
|
||||
.fb_blank = aafb_blank,
|
||||
.fb_fillrect = cfb_fillrect,
|
||||
.fb_copyarea = cfb_copyarea,
|
||||
.fb_imageblit = cfb_imageblit,
|
||||
.fb_cursor = aafb_cursor,
|
||||
};
|
||||
|
||||
static int __init init_one(int slot)
|
||||
static int pmagaafb_probe(struct device *dev)
|
||||
{
|
||||
unsigned long base_addr = CKSEG1ADDR(get_tc_base_addr(slot));
|
||||
struct aafb_info *ip = &my_fb_info[slot];
|
||||
struct tc_dev *tdev = to_tc_dev(dev);
|
||||
resource_size_t start, len;
|
||||
struct fb_info *info;
|
||||
struct aafb_par *par;
|
||||
int err;
|
||||
|
||||
memset(ip, 0, sizeof(struct aafb_info));
|
||||
info = framebuffer_alloc(sizeof(struct aafb_par), dev);
|
||||
if (!info) {
|
||||
printk(KERN_ERR "%s: Cannot allocate memory\n", dev_name(dev));
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/*
|
||||
* Framebuffer display memory base address and friends.
|
||||
*/
|
||||
ip->bt455 = (struct bt455_regs *) (base_addr + PMAG_AA_BT455_OFFSET);
|
||||
ip->bt431 = (struct bt431_regs *) (base_addr + PMAG_AA_BT431_OFFSET);
|
||||
ip->fb_start = base_addr + PMAG_AA_ONBOARD_FBMEM_OFFSET;
|
||||
ip->fb_size = 2048 * 1024; /* fb_fix_screeninfo.smem_length
|
||||
seems to be physical */
|
||||
ip->fb_line_length = 2048;
|
||||
par = info->par;
|
||||
dev_set_drvdata(dev, info);
|
||||
|
||||
/*
|
||||
* Let there be consoles..
|
||||
*/
|
||||
strcpy(ip->info.modename, "PMAG-AA");
|
||||
ip->info.node = -1;
|
||||
ip->info.flags = FBINFO_FLAG_DEFAULT;
|
||||
ip->info.fbops = &aafb_ops;
|
||||
ip->info.disp = &ip->disp;
|
||||
ip->info.changevar = NULL;
|
||||
ip->info.switch_con = &aafb_switch;
|
||||
ip->info.updatevar = &aafb_update_var;
|
||||
ip->info.blank = &aafb_blank;
|
||||
info->fbops = &aafb_ops;
|
||||
info->fix = aafb_fix;
|
||||
info->var = aafb_defined;
|
||||
info->flags = FBINFO_DEFAULT;
|
||||
|
||||
aafb_set_disp(&ip->disp, currcon, ip);
|
||||
/* Request the I/O MEM resource. */
|
||||
start = tdev->resource.start;
|
||||
len = tdev->resource.end - start + 1;
|
||||
if (!request_mem_region(start, len, dev_name(dev))) {
|
||||
printk(KERN_ERR "%s: Cannot reserve FB region\n",
|
||||
dev_name(dev));
|
||||
err = -EBUSY;
|
||||
goto err_alloc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Configure the RAM DACs.
|
||||
*/
|
||||
bt455_erase_cursor(ip->bt455);
|
||||
/* MMIO mapping setup. */
|
||||
info->fix.mmio_start = start + PMAG_AA_BT455_OFFSET;
|
||||
par->mmio = ioremap_nocache(info->fix.mmio_start, info->fix.mmio_len);
|
||||
if (!par->mmio) {
|
||||
printk(KERN_ERR "%s: Cannot map MMIO\n", dev_name(dev));
|
||||
err = -ENOMEM;
|
||||
goto err_resource;
|
||||
}
|
||||
par->bt455 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT455_OFFSET;
|
||||
par->bt431 = par->mmio - PMAG_AA_BT455_OFFSET + PMAG_AA_BT431_OFFSET;
|
||||
|
||||
/* Frame buffer mapping setup. */
|
||||
info->fix.smem_start = start + PMAG_AA_ONBOARD_FBMEM_OFFSET;
|
||||
info->screen_base = ioremap_nocache(info->fix.smem_start,
|
||||
info->fix.smem_len);
|
||||
if (!info->screen_base) {
|
||||
printk(KERN_ERR "%s: Cannot map FB\n", dev_name(dev));
|
||||
err = -ENOMEM;
|
||||
goto err_mmio_map;
|
||||
}
|
||||
info->screen_size = info->fix.smem_len;
|
||||
|
||||
/* Init colormap. */
|
||||
bt455_write_cmap_entry(ip->bt455, 0, 0x00, 0x00, 0x00);
|
||||
bt455_write_cmap_entry(ip->bt455, 1, 0x0f, 0x0f, 0x0f);
|
||||
bt455_write_cmap_entry(par->bt455, 0, 0x00, 0x00, 0x00);
|
||||
bt455_write_cmap_entry(par->bt455, 1, 0x0f, 0x0f, 0x0f);
|
||||
|
||||
/* Init hardware cursor. */
|
||||
bt431_init_cursor(ip->bt431);
|
||||
aafb_cursor_init(ip);
|
||||
bt431_erase_cursor(par->bt431);
|
||||
bt431_init_cursor(par->bt431);
|
||||
|
||||
/* Clear the screen. */
|
||||
memset ((void *)ip->fb_start, 0, ip->fb_size);
|
||||
err = register_framebuffer(info);
|
||||
if (err < 0) {
|
||||
printk(KERN_ERR "%s: Cannot register framebuffer\n",
|
||||
dev_name(dev));
|
||||
goto err_smem_map;
|
||||
}
|
||||
|
||||
if (register_framebuffer(&ip->info) < 0)
|
||||
return -EINVAL;
|
||||
get_device(dev);
|
||||
|
||||
printk(KERN_INFO "fb%d: %s frame buffer in TC slot %d\n",
|
||||
GET_FB_IDX(ip->info.node), ip->info.modename, slot);
|
||||
pr_info("fb%d: %s frame buffer device at %s\n",
|
||||
info->node, info->fix.id, dev_name(dev));
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
err_smem_map:
|
||||
iounmap(info->screen_base);
|
||||
|
||||
err_mmio_map:
|
||||
iounmap(par->mmio);
|
||||
|
||||
err_resource:
|
||||
release_mem_region(start, len);
|
||||
|
||||
err_alloc:
|
||||
framebuffer_release(info);
|
||||
return err;
|
||||
}
|
||||
|
||||
static int __exit exit_one(int slot)
|
||||
static int __exit pmagaafb_remove(struct device *dev)
|
||||
{
|
||||
struct aafb_info *ip = &my_fb_info[slot];
|
||||
|
||||
if (unregister_framebuffer(&ip->info) < 0)
|
||||
return -EINVAL;
|
||||
struct tc_dev *tdev = to_tc_dev(dev);
|
||||
struct fb_info *info = dev_get_drvdata(dev);
|
||||
struct aafb_par *par = info->par;
|
||||
resource_size_t start, len;
|
||||
|
||||
put_device(dev);
|
||||
unregister_framebuffer(info);
|
||||
iounmap(info->screen_base);
|
||||
iounmap(par->mmio);
|
||||
start = tdev->resource.start;
|
||||
len = tdev->resource.end - start + 1;
|
||||
release_mem_region(start, len);
|
||||
framebuffer_release(info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialise the framebuffer.
|
||||
*/
|
||||
int __init pmagaafb_init(void)
|
||||
static const struct tc_device_id pmagaafb_tc_table[] = {
|
||||
{ "DEC ", "PMAG-AA " },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(tc, pmagaafb_tc_table);
|
||||
|
||||
static struct tc_driver pmagaafb_driver = {
|
||||
.id_table = pmagaafb_tc_table,
|
||||
.driver = {
|
||||
.name = "pmagaafb",
|
||||
.bus = &tc_bus_type,
|
||||
.probe = pmagaafb_probe,
|
||||
.remove = __exit_p(pmagaafb_remove),
|
||||
},
|
||||
};
|
||||
|
||||
static int __init pmagaafb_init(void)
|
||||
{
|
||||
int sid;
|
||||
int found = 0;
|
||||
|
||||
while ((sid = search_tc_card("PMAG-AA")) >= 0) {
|
||||
found = 1;
|
||||
claim_tc_card(sid);
|
||||
init_one(sid);
|
||||
}
|
||||
|
||||
return found ? 0 : -ENXIO;
|
||||
#ifndef MODULE
|
||||
if (fb_get_options("pmagaafb", NULL))
|
||||
return -ENXIO;
|
||||
#endif
|
||||
return tc_register_driver(&pmagaafb_driver);
|
||||
}
|
||||
|
||||
static void __exit pmagaafb_exit(void)
|
||||
{
|
||||
int sid;
|
||||
|
||||
while ((sid = search_tc_card("PMAG-AA")) >= 0) {
|
||||
exit_one(sid);
|
||||
release_tc_card(sid);
|
||||
}
|
||||
tc_unregister_driver(&pmagaafb_driver);
|
||||
}
|
||||
|
||||
module_init(pmagaafb_init);
|
||||
module_exit(pmagaafb_exit);
|
||||
|
||||
MODULE_AUTHOR(DRIVER_AUTHOR);
|
||||
MODULE_DESCRIPTION(DRIVER_DESCRIPTION);
|
||||
MODULE_LICENSE("GPL");
|
||||
#ifdef MODULE
|
||||
module_init(pmagaafb_init);
|
||||
module_exit(pmagaafb_exit);
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user