Maciej W. Rozycki wrote: > On Sun, 21 Sep 2003, Thiemo Seufer wrote: > > > @@ -37,8 +37,7 @@ static inline void bt455_read_cmap_entry > > u8* red, u8* green, u8* blue) > > { > > bt455_select_reg(regs, cr); > > - > > - mb(); > > + rmb(); > > *red = regs->addr_cmap_data & 0x0f; > > rmb(); > > *green = regs->addr_cmap_data & 0x0f; > > I do think it has to be an mb() as the first access is a write and the > second one is a read. > > You may also consider using ISO C initializers for struct members -- I > can do the conversion myself, but since you are actively working on the > code right now, I guess it might give you an unnecessary burden of chasing > the changing master sources. An updated patch is appended. Thiemo diff -abdpruNPX /bigdisk/src/dontdiff linux-orig/drivers/video/bt431.h linux/drivers/video/bt431.h --- linux-orig/drivers/video/bt431.h Tue Sep 16 17:05:14 2003 +++ linux/drivers/video/bt431.h Sun Sep 21 22:39:12 2003 @@ -73,22 +73,42 @@ static inline u8 bt431_get_value(u16 val static inline void bt431_select_reg(struct bt431_regs *regs, int ir) { + /* + * The compiler splits the write in two bytes without these + * helper variables. + */ + volatile u16 *lo = &(regs->addr_lo); + volatile u16 *hi = &(regs->addr_hi); + mb(); - regs->addr_lo = bt431_set_value(ir & 0xff); - regs->addr_hi = bt431_set_value((ir >> 8) & 0xff); + *lo = bt431_set_value(ir & 0xff); + wmb(); + *hi = bt431_set_value((ir >> 8) & 0xff); } /* Autoincrement read/write. */ static inline u8 bt431_read_reg_inc(struct bt431_regs *regs) { + /* + * The compiler splits the write in two bytes without the + * helper variable. + */ + volatile u16 *r = &(regs->addr_reg); + mb(); - return bt431_get_value(regs->addr_reg); + return bt431_get_value(*r); } static inline void bt431_write_reg_inc(struct bt431_regs *regs, u8 value) { + /* + * The compiler splits the write in two bytes without the + * helper variable. + */ + volatile u16 *r = &(regs->addr_reg); + mb(); - regs->addr_reg = bt431_set_value(value); + *r = bt431_set_value(value); } static inline u8 bt431_read_reg(struct bt431_regs *regs, int ir) @@ -97,23 +117,35 @@ static inline u8 bt431_read_reg(struct b return bt431_read_reg_inc(regs); } -static inline void bt431_write_reg(struct bt431_regs *regs, int ir, u16 value) +static inline void bt431_write_reg(struct bt431_regs *regs, int ir, u8 value) { bt431_select_reg(regs, ir); bt431_write_reg_inc(regs, value); } -/* Autoincremented read/write for the cursor map */ +/* Autoincremented read/write for the cursor map. */ static inline u16 bt431_read_cmap_inc(struct bt431_regs *regs) { + /* + * The compiler splits the write in two bytes without the + * helper variable. + */ + volatile u16 *r = &(regs->addr_cmap); + mb(); - return regs->addr_cmap; + return *r; } static inline void bt431_write_cmap_inc(struct bt431_regs *regs, u16 value) { + /* + * The compiler splits the write in two bytes without the + * helper variable. + */ + volatile u16 *r = &(regs->addr_cmap); + mb(); - regs->addr_cmap = value; + *r = value; } static inline u16 bt431_read_cmap(struct bt431_regs *regs, int cr) @@ -130,10 +162,9 @@ static inline void bt431_write_cmap(stru static inline void bt431_enable_cursor(struct bt431_regs *regs) { -/* bt431_write_reg(regs, BT431_REG_CMD, + bt431_write_reg(regs, BT431_REG_CMD, BT431_CMD_CURS_ENABLE | BT431_CMD_OR_CURSORS | BT431_CMD_4_1_MUX | BT431_CMD_THICK_1); -*/ bt431_write_reg(regs, BT431_REG_CMD, BT431_CMD_CURS_ENABLE); } static inline void bt431_erase_cursor(struct bt431_regs *regs) @@ -166,64 +197,40 @@ static inline void bt431_position_cursor bt431_write_reg_inc(regs, (y >> 8) & 0x0f); /* BT431_REG_CYHI */ } -/*u16 _bt431_default_cursor[64 * 8] = { - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0xffff, 0, 0, 0, 0, 0, 0, 0, - 0, -}; -*/ -static inline void bt431_load_cursor_sprite(struct bt431_regs *regs) +static inline void bt431_set_font(struct bt431_regs *regs, u8 fgc, + u16 width, u16 height) { int i; + u16 fgp = fgc ? 0xffff : 0x0000; + u16 bgp = fgc ? 0x0000 : 0xffff; bt431_select_reg(regs, BT431_REG_CRAM_BASE); - for (i = 0; i < 64 * 8; i++) - bt431_write_cmap_inc(regs, ((i < 16 * 8) && (i % 8)) ? 0xffff : 0); + for (i = BT431_REG_CRAM_BASE; i <= BT431_REG_CRAM_END; i++) { + u16 value; + + if (height << 6 <= i << 3) + value = bgp; + else if (width <= i % 8 << 3) + value = bgp; + else if (((width >> 3) & 0xffff) > i % 8) + value = fgp; + else + value = fgp & ~(bgp << (width % 8 << 1)); + + bt431_write_cmap_inc(regs, value); + } } static inline void bt431_init_cursor(struct bt431_regs *regs) { - bt431_write_reg(regs, BT431_REG_CMD, - BT431_CMD_CURS_ENABLE | BT431_CMD_OR_CURSORS - | BT431_CMD_4_1_MUX | BT431_CMD_THICK_1); - - /* home cursor */ -#if 0 - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CXLO */ - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CXHI */ - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CYLO */ - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CYHI */ -#endif - bt431_write_reg_inc(regs, 0x80); /* BT431_REG_CXLO */ - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CXHI */ - bt431_write_reg_inc(regs, 0x80); /* BT431_REG_CYLO */ - bt431_write_reg_inc(regs, 0x00); /* BT431_REG_CYHI */ - /* no crosshair window */ + bt431_select_reg(regs, BT431_REG_WXLO); bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WXLO */ bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WXHI */ bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WYLO */ bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WYHI */ -// bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WWLO */ - bt431_write_reg_inc(regs, 0x01); /* BT431_REG_WWLO */ + bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WWLO */ bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WWHI */ -// bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WHLO */ - bt431_write_reg_inc(regs, 0x01); /* BT431_REG_WHLO */ + bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WHLO */ bt431_write_reg_inc(regs, 0x00); /* BT431_REG_WHHI */ - - bt431_load_cursor_sprite(regs); } diff -abdpruNPX /bigdisk/src/dontdiff linux-orig/drivers/video/bt455.h linux/drivers/video/bt455.h --- linux-orig/drivers/video/bt455.h Tue Sep 16 17:05:14 2003 +++ linux/drivers/video/bt455.h Tue Sep 23 10:26:53 2003 @@ -37,7 +37,6 @@ static inline void bt455_read_cmap_entry u8* red, u8* green, u8* blue) { bt455_select_reg(regs, cr); - mb(); *red = regs->addr_cmap_data & 0x0f; rmb(); @@ -50,7 +49,6 @@ static inline void bt455_write_cmap_entr u8 red, u8 green, u8 blue) { bt455_select_reg(regs, cr); - wmb(); regs->addr_cmap_data = red & 0x0f; wmb(); @@ -59,10 +57,11 @@ static inline void bt455_write_cmap_entr regs->addr_cmap_data = blue & 0x0f; } -static inline void bt455_write_ovly_entry(struct bt455_regs *regs, +static inline void bt455_write_ovly_entry(struct bt455_regs *regs, int cr, u8 red, u8 green, u8 blue) { - mb(); + bt455_select_reg(regs, cr); + wmb(); regs->addr_ovly = red & 0x0f; wmb(); regs->addr_ovly = green & 0x0f; @@ -82,10 +81,15 @@ static inline void bt455_set_cursor(stru static inline void bt455_erase_cursor(struct bt455_regs *regs) { -// bt455_write_cmap_entry(regs, 8, 0x00, 0x00, 0x00); -// bt455_write_cmap_entry(regs, 9, 0x00, 0x00, 0x00); - bt455_write_cmap_entry(regs, 8, 0x03, 0x03, 0x03); - bt455_write_cmap_entry(regs, 9, 0x07, 0x07, 0x07); + /* bt455_write_cmap_entry(regs, 8, 0x00, 0x00, 0x00); */ + /* bt455_write_cmap_entry(regs, 9, 0x00, 0x00, 0x00); */ + bt455_write_ovly_entry(regs, 8, 0x03, 0x03, 0x03); + bt455_write_ovly_entry(regs, 9, 0x07, 0x07, 0x07); - bt455_write_ovly_entry(regs, 0x09, 0x09, 0x09); + wmb(); + regs->addr_ovly = 0x09; + wmb(); + regs->addr_ovly = 0x09; + wmb(); + regs->addr_ovly = 0x09; } diff -abdpruNPX /bigdisk/src/dontdiff linux-orig/drivers/video/pmag-aa-fb.c linux/drivers/video/pmag-aa-fb.c --- linux-orig/drivers/video/pmag-aa-fb.c Tue Sep 16 17:05:14 2003 +++ linux/drivers/video/pmag-aa-fb.c Tue Sep 23 10:26:52 2003 @@ -13,10 +13,14 @@ * Public License. See the file COPYING in the main directory of this * archive for more details. * - * Version 0.01 2002/09/28 first try to get a PMAG-AA running + * 2002-09-28 Karsten Merker <merker@linuxtag.org> + * Version 0.01: First try to get a PMAG-AA running. * - * 2003/02/24 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> - * Code cleanup. + * 2003-02-24 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> + * Version 0.02: Major code cleanup. + * + * 2003-09-21 Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de> + * Hardware cursor support. */ #include <linux/module.h> #include <linux/kernel.h> @@ -30,22 +34,26 @@ #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-mfb.h> #include <video/fbcon-cfb8.h> #include "bt455.h" #include "bt431.h" /* Version information */ -#define DRIVER_VERSION "v0.02" +#define DRIVER_VERSION "0.02" #define DRIVER_AUTHOR "Karsten Merker <merker@linuxtag.org>" -#define DRIVER_DESC "PMAG-AA Framebuffer Driver" +#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). @@ -63,9 +71,23 @@ */ #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; +}; + +#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; @@ -81,57 +103,174 @@ static struct aafb_info my_fb_info[3]; static struct aafb_par { } current_par; -static int currcon = 0; +static int currcon = -1; -static void aafb_get_par(struct aafb_par *par) +static void aafb_set_cursor(struct aafb_info *info, int on) { - *par = current_par; + struct aafb_cursor *c = &info->cursor; + + if (on) { + bt431_position_cursor(info->bt431, c->x, c->y); + bt431_enable_cursor(info->bt431); + } else + bt431_erase_cursor(info->bt431); } -static void aafb_encode_fix(struct fb_fix_screeninfo *fix, - struct aafb_par *par, struct aafb_info *info) +static void aafbcon_cursor(struct display *disp, int mode, int x, int y) { - memset(fix, 0, sizeof(struct fb_fix_screeninfo)); - strcpy(fix->id, "PMAG-AA"); + struct aafb_info *info = (struct aafb_info *)disp->fb_info; + struct aafb_cursor *c = &info->cursor; - fix->smem_start = info->fb_start; - fix->smem_len = info->fb_size; - fix->type = FB_TYPE_PACKED_PIXELS; - fix->visual = FB_VISUAL_MONO10; - fix->line_length = info->fb_line_length; - fix->accel = FB_ACCEL_NONE; + 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); + + if (width > 64 || height > 64 || width < 0 || height < 0) + 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); +} + +static void __init aafb_cursor_init(struct aafb_info *info) +{ + struct aafb_cursor *c = &info->cursor; + + 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; + + 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_par par; + struct aafb_info *ip = (struct aafb_info *)info; - aafb_get_par(&par); - aafb_encode_fix(fix, &par, (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(int con, struct fb_info *info) +static void aafb_set_disp(struct display *disp, int con, + struct aafb_info *info) { struct fb_fix_screeninfo fix; - struct display *disp = (con < 0) ? info->disp : (fb_display + con); - aafb_get_fix(&fix, con, info); + 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; - disp->screen_base = (char *) fix.smem_start; + 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 = fix.line_length; + disp->line_length = fix.line_length; + disp->next_line = 2048; disp->can_soft_blank = 1; disp->inverse = 0; -// disp->scrollmode = SCROLL_YREDRAW; - disp->dispsw = &fbcon_cfb8; + 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, @@ -147,14 +286,38 @@ static int aafb_get_cmap(struct fb_cmap 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 inode *inode, struct file *file, u32 cmd, + unsigned long arg, int con, struct fb_info *info) +{ + /* 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(con, info); + aafb_set_disp(new, con, ip); return 0; } @@ -218,6 +381,12 @@ static int aafb_set_var(struct fb_var_sc 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); + return 0; } @@ -229,15 +398,17 @@ static void aafb_blank(int blank, struct 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); } 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 + .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 }; static int __init init_one(int slot) @@ -258,20 +429,6 @@ static int __init init_one(int slot) ip->fb_line_length = 2048; /* - * Configure the RAM DACs. - */ -// TODO -// bt455_erase_cursor(ip->bt455); -// bt455_set_cursor(ip->bt455); -// bt431_erase_cursor(ip->bt431); -// bt431_init_cursor(ip->bt431); -// bt431_position_cursor(ip->bt431, 16, 16); - - /* Init colormap. */ - bt455_write_cmap_entry(ip->bt455, 0, 0x00, 0x00, 0x00); - bt455_write_cmap_entry(ip->bt455, 1, 0x0f, 0x0f, 0x0f); - - /* * Let there be consoles.. */ strcpy(ip->info.modename, "PMAG-AA"); @@ -284,8 +441,20 @@ static int __init init_one(int slot) ip->info.updatevar = &aafb_update_var; ip->info.blank = &aafb_blank; - aafb_set_disp(-1, &ip->info); - aafb_set_var(&ip->disp.var, -1, &ip->info); + aafb_set_disp(&ip->disp, currcon, ip); + + /* + * Configure the RAM DACs. + */ + bt455_erase_cursor(ip->bt455); + + /* Init colormap. */ + bt455_write_cmap_entry(ip->bt455, 0, 0x00, 0x00, 0x00); + bt455_write_cmap_entry(ip->bt455, 1, 0x0f, 0x0f, 0x0f); + + /* Init hardware cursor. */ + bt431_init_cursor(ip->bt431); + aafb_cursor_init(ip); /* Clear the screen. */ memset ((void *)ip->fb_start, 0, ip->fb_size); @@ -293,7 +462,7 @@ static int __init init_one(int slot) if (register_framebuffer(&ip->info) < 0) return -EINVAL; - printk(KERN_INFO "fb%d: %s frame buffer device in TC slot %d\n", + printk(KERN_INFO "fb%d: %s frame buffer in TC slot %d\n", GET_FB_IDX(ip->info.node), ip->info.modename, slot); return 0; @@ -336,4 +505,10 @@ static void __exit pmagaafb_exit(void) } } +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESCRIPTION); MODULE_LICENSE("GPL"); +#ifdef MODULE +module_init(pmagaafb_init); +module_exit(pmagaafb_exit); +#endif