patch-2.1.109 linux/drivers/video/sbusfb.c
Next file: linux/drivers/video/sbusfb.h
Previous file: linux/drivers/video/retz3fb.c
Back to the patch index
Back to the overall index
- Lines: 1013
- Date:
Fri Jul 10 15:18:32 1998
- Orig file:
v2.1.108/linux/drivers/video/sbusfb.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.108/linux/drivers/video/sbusfb.c linux/drivers/video/sbusfb.c
@@ -0,0 +1,1012 @@
+/*
+ * linux/drivers/video/sbusfb.c -- SBUS or UPA based frame buffer device
+ *
+ * Copyright (C) 1998 Jakub Jelinek
+ *
+ * This driver is partly based on the Open Firmware console driver
+ *
+ * Copyright (C) 1997 Geert Uytterhoeven
+ *
+ * and SPARC console subsystem
+ *
+ * Copyright (C) 1995 Peter Zaitcev (zaitcev@lab.ipmce.su)
+ * Copyright (C) 1995-1997 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1995-1996 Miguel de Icaza (miguel@nuclecu.unam.mx)
+ * Copyright (C) 1996 Dave Redman (djhr@tadpole.co.uk)
+ * Copyright (C) 1996-1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 1996 Eddie C. Dost (ecd@skynet.be)
+ *
+ * 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/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/malloc.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/selection.h>
+#include <linux/init.h>
+#include <linux/console.h>
+#include <linux/kd.h>
+#include <linux/vt_kern.h>
+
+#include <asm/uaccess.h>
+
+#include "sbusfb.h"
+
+ /*
+ * Interface used by the world
+ */
+
+void sbusfb_init(void);
+void sbusfb_setup(char *options, int *ints);
+
+static int currcon;
+static int nomargins __initdata = 0;
+static struct {
+ int depth;
+ int xres, yres;
+ int x_margin, y_margin;
+} def_margins [] __initdata = {
+ { 8, 1280, 1024, 64, 80 },
+ { 8, 1152, 1024, 64, 80 },
+ { 8, 1152, 900, 64, 18 },
+ { 8, 1024, 768, 0, 0 },
+ { 8, 800, 600, 16, 12 },
+ { 8, 640, 480, 0, 0 },
+ { 1, 1152, 900, 8, 18 },
+ { 0 },
+};
+
+static int sbusfb_open(struct fb_info *info, int user);
+static int sbusfb_release(struct fb_info *info, int user);
+static int sbusfb_mmap(struct fb_info *info, struct file *file,
+ struct vm_area_struct *vma);
+static int sbusfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info);
+static int sbusfb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sbusfb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info);
+static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info);
+static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con, struct fb_info *info);
+static void sbusfb_cursor(struct display *p, int mode, int x, int y);
+static void sbusfb_clear_margin(struct display *p, int s);
+extern int io_remap_page_range(unsigned long from, unsigned long offset,
+ unsigned long size, pgprot_t prot, int space);
+
+
+ /*
+ * Interface to the low level console driver
+ */
+
+static int sbusfbcon_switch(int con, struct fb_info *info);
+static int sbusfbcon_updatevar(int con, struct fb_info *info);
+static void sbusfbcon_blank(int blank, struct fb_info *info);
+
+
+ /*
+ * Internal routines
+ */
+
+static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp, struct fb_info *info);
+static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info);
+static void do_install_cmap(int con, struct fb_info *info);
+
+static struct fb_ops sbusfb_ops = {
+ sbusfb_open, sbusfb_release, sbusfb_get_fix, sbusfb_get_var, sbusfb_set_var,
+ sbusfb_get_cmap, sbusfb_set_cmap, sbusfb_pan_display, sbusfb_ioctl, sbusfb_mmap
+};
+
+
+ /*
+ * Open/Release the frame buffer device
+ */
+
+static int sbusfb_open(struct fb_info *info, int user)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (user) {
+ if (fb->open) return -EBUSY;
+ fb->mmaped = 0;
+ fb->open = 1;
+ fb->vtconsole = -1;
+ } else
+ fb->consolecnt++;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int sbusfb_release(struct fb_info *info, int user)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (user) {
+ if (fb->vtconsole != -1) {
+ vt_cons[fb->vtconsole]->vc_mode = KD_TEXT;
+ if (fb->mmaped)
+ sbusfb_clear_margin(&fb_display[fb->vtconsole], 0);
+ }
+ fb->open = 0;
+ } else
+ fb->consolecnt--;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static int sbusfb_mmap(struct fb_info *info, struct file *file,
+ struct vm_area_struct *vma)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+ unsigned int size, page, r, map_size;
+ unsigned long map_offset = 0;
+ int i;
+
+ size = vma->vm_end - vma->vm_start;
+ if (vma->vm_offset & ~PAGE_MASK)
+ return -ENXIO;
+
+ /* To stop the swapper from even considering these pages */
+ vma->vm_flags |= (VM_SHM| VM_LOCKED);
+
+#ifdef __sparc_v9__
+ /* Align it as much as desirable */
+ {
+ int j, max = -1, alignment;
+
+ map_offset = vma->vm_offset+size;
+ for (i = 0; fb->mmap_map[i].size; i++) {
+ if (fb->mmap_map[i].voff < vma->vm_offset)
+ continue;
+ if (fb->mmap_map[i].voff >= map_offset)
+ break;
+ if (max < 0 || fb->mmap_map[i].size > fb->mmap_map[max].size)
+ max = i;
+ }
+ if (max >= 0) {
+ j = fb->mmap_map[max].size;
+ if (fb->mmap_map[max].voff + j > map_offset)
+ j = map_offset - fb->mmap_map[max].voff;
+ for (alignment = 0x400000; alignment > PAGE_SIZE; alignment >>= 3)
+ if (j >= alignment && !(fb->mmap_map[max].poff & (alignment - 1)))
+ break;
+ if (alignment > PAGE_SIZE) {
+ j = alignment;
+ alignment = j - ((vma->vm_start + fb->mmap_map[max].voff - vma->vm_offset) & (j - 1));
+ if (alignment != j) {
+ struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start);
+ if (!vmm || vmm->vm_start >= vma->vm_end + alignment) {
+ vma->vm_start += alignment;
+ vma->vm_end += alignment;
+ }
+ }
+ }
+ }
+ }
+#endif
+
+ /* Each page, see which map applies */
+ for (page = 0; page < size; ){
+ map_size = 0;
+ for (i = 0; fb->mmap_map[i].size; i++)
+ if (fb->mmap_map[i].voff == vma->vm_offset+page) {
+ map_size = fb->mmap_map[i].size;
+ map_offset = (fb->physbase + fb->mmap_map[i].poff) & _PAGE_PADDR;
+ break;
+ }
+ if (!map_size){
+ page += PAGE_SIZE;
+ continue;
+ }
+ if (page + map_size > size)
+ map_size = size - page;
+ r = io_remap_page_range (vma->vm_start+page, map_offset, map_size, vma->vm_page_prot, fb->iospace);
+ if (r)
+ return -EAGAIN;
+ page += map_size;
+ }
+
+ vma->vm_file = file;
+ file->f_count++;
+ vma->vm_flags |= VM_IO;
+ if (!fb->mmaped) {
+ fb->mmaped = 1;
+ if (fb->consolecnt && fb_display[fb->lastconsole].fb_info == info) {
+ fb->vtconsole = fb->lastconsole;
+ vt_cons [fb->lastconsole]->vc_mode = KD_GRAPHICS;
+ } else if (fb->unblank && !fb->blanked)
+ (*fb->unblank)(fb);
+ }
+ return 0;
+}
+
+static void sbusfb_clear_margin(struct display *p, int s)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ if (fb->fill) {
+ unsigned short rects [16];
+
+ rects [0] = 0;
+ rects [1] = 0;
+ rects [2] = fb->var.xres_virtual;
+ rects [3] = fb->y_margin;
+ rects [4] = 0;
+ rects [5] = fb->y_margin;
+ rects [6] = fb->x_margin;
+ rects [7] = fb->var.yres_virtual;
+ rects [8] = fb->var.xres_virtual - fb->x_margin;
+ rects [9] = fb->y_margin;
+ rects [10] = fb->var.xres_virtual;
+ rects [11] = fb->var.yres_virtual;
+ rects [12] = fb->x_margin;
+ rects [13] = fb->var.yres_virtual - fb->y_margin;
+ rects [14] = fb->var.xres_virtual - fb->x_margin;
+ rects [15] = fb->var.yres_virtual;
+ (*fb->fill)(fb, s, 4, rects);
+ } else {
+ unsigned char *fb_base = p->screen_base, *q;
+ int skip_bytes = fb->y_margin * fb->var.xres_virtual;
+ int scr_size = fb->var.xres_virtual * fb->var.yres_virtual;
+ int h, he, incr, size;
+
+ he = fb->var.yres;
+ if (fb->var.bits_per_pixel == 1) {
+ fb_base -= (skip_bytes + fb->x_margin) / 8;
+ skip_bytes /= 8;
+ scr_size /= 8;
+ memset (fb_base, ~0, skip_bytes - fb->x_margin / 8);
+ memset (fb_base + scr_size - skip_bytes + fb->x_margin / 8, ~0, skip_bytes - fb->x_margin / 8);
+ incr = fb->var.xres_virtual / 8;
+ size = fb->x_margin / 8 * 2;
+ for (q = fb_base + skip_bytes - fb->x_margin / 8, h = 0;
+ h <= he; q += incr, h++)
+ memset (q, ~0, size);
+ } else {
+ fb_base -= (skip_bytes + fb->x_margin);
+ memset (fb_base, attr_bg_col(s), skip_bytes - fb->x_margin);
+ memset (fb_base + scr_size - skip_bytes + fb->x_margin, attr_bg_col(s), skip_bytes - fb->x_margin);
+ incr = fb->var.xres_virtual;
+ size = fb->x_margin * 2;
+ for (q = fb_base + skip_bytes - fb->x_margin, h = 0;
+ h <= he; q += incr, h++)
+ memset (q, attr_bg_col(s), size);
+ }
+ }
+ if (fb->switch_from_graph)
+ (*fb->switch_from_graph)(fb);
+}
+
+static void sbusfb_disp_setup(struct display *p)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ if (fb->setup)
+ fb->setup(p);
+ sbusfb_clear_margin(p, 0);
+}
+
+ /*
+ * Get the Fixed Part of the Display
+ */
+
+static int sbusfb_get_fix(struct fb_fix_screeninfo *fix, int con,
+ struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ memcpy(fix, &fb->fix, sizeof(struct fb_fix_screeninfo));
+ return 0;
+}
+
+ /*
+ * Get the User Defined Part of the Display
+ */
+
+static int sbusfb_get_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+ return 0;
+}
+
+ /*
+ * Set the User Defined Part of the Display
+ */
+
+static int sbusfb_set_var(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+#if 1
+ return -EINVAL;
+#else
+ struct display *display;
+ int oldbpp = -1, err;
+ int activate = var->activate;
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (con >= 0)
+ display = &fb_display[con];
+ else
+ display = &fb->disp; /* used during initialization */
+
+ if (var->xres > fb->var.xres || var->yres > fb->var.yres ||
+ var->xres_virtual > fb->var.xres_virtual ||
+ var->yres_virtual > fb->var.yres_virtual ||
+ var->bits_per_pixel > fb->var.bits_per_pixel ||
+ var->nonstd ||
+ (var->vmode & FB_VMODE_MASK) != FB_VMODE_NONINTERLACED)
+ return -EINVAL;
+ memcpy(var, &fb->var, sizeof(struct fb_var_screeninfo));
+
+ if ((activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) {
+ oldbpp = display->var.bits_per_pixel;
+ display->var = *var;
+ }
+ if (oldbpp != var->bits_per_pixel) {
+ if ((err = fb_alloc_cmap(&display->cmap, 0, 0)))
+ return err;
+ do_install_cmap(con, info);
+ }
+ return 0;
+#endif
+}
+
+ /*
+ * Pan or Wrap the Display
+ *
+ * This call looks only at xoffset, yoffset and the FB_VMODE_YWRAP flag
+ */
+
+static int sbusfb_pan_display(struct fb_var_screeninfo *var, int con,
+ struct fb_info *info)
+{
+ if (var->xoffset || var->yoffset)
+ return -EINVAL;
+ else
+ return 0;
+}
+
+ /*
+ * Hardware cursor
+ */
+
+static int sbus_hw_scursor (struct fbcursor *cursor, struct fb_info_sbusfb *fb)
+{
+ int op;
+ int i, bytes = 0;
+ struct fbcursor f;
+ char red[2], green[2], blue[2];
+
+ if (copy_from_user (&f, cursor, sizeof(struct fbcursor)))
+ return -EFAULT;
+ op = f.set;
+ if (op & FB_CUR_SETSHAPE){
+ if ((u32) f.size.fbx > fb->cursor.hwsize.fbx)
+ return -EINVAL;
+ if ((u32) f.size.fby > fb->cursor.hwsize.fby)
+ return -EINVAL;
+ if (f.size.fbx > 32)
+ bytes = f.size.fby << 3;
+ else
+ bytes = f.size.fby << 2;
+ }
+ if (op & FB_CUR_SETCMAP){
+ if (f.cmap.index || f.cmap.count != 2)
+ return -EINVAL;
+ if (copy_from_user (red, f.cmap.red, 2) ||
+ copy_from_user (green, f.cmap.green, 2) ||
+ copy_from_user (blue, f.cmap.blue, 2))
+ return -EFAULT;
+ }
+ if (op & FB_CUR_SETCMAP)
+ (*fb->setcursormap) (fb, red, green, blue);
+ if (op & FB_CUR_SETSHAPE){
+ u32 u;
+
+ fb->cursor.size = f.size;
+ memset ((void *)&fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+ if (copy_from_user (fb->cursor.bits [0], f.mask, bytes) ||
+ copy_from_user (fb->cursor.bits [1], f.image, bytes))
+ return -EFAULT;
+ if (f.size.fbx <= 32) {
+ u = ~(0xffffffff >> f.size.fbx);
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][i] &= u;
+ fb->cursor.bits [1][i] &= fb->cursor.bits [0][i];
+ }
+ } else {
+ u = ~(0xffffffff >> (f.size.fbx - 32));
+ for (i = fb->cursor.size.fby - 1; i >= 0; i--) {
+ fb->cursor.bits [0][2*i+1] &= u;
+ fb->cursor.bits [1][2*i] &= fb->cursor.bits [0][2*i];
+ fb->cursor.bits [1][2*i+1] &= fb->cursor.bits [0][2*i+1];
+ }
+ }
+ (*fb->setcurshape) (fb);
+ }
+ if (op & (FB_CUR_SETCUR | FB_CUR_SETPOS | FB_CUR_SETHOT)){
+ if (op & FB_CUR_SETCUR)
+ fb->cursor.enable = f.enable;
+ if (op & FB_CUR_SETPOS)
+ fb->cursor.cpos = f.pos;
+ if (op & FB_CUR_SETHOT)
+ fb->cursor.chot = f.hot;
+ (*fb->setcursor) (fb);
+ }
+ return 0;
+}
+
+static unsigned char hw_cursor_cmap[2] = { 0, 0xff };
+
+static void sbusfb_cursor(struct display *p, int mode, int x, int y)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfod(p);
+
+ switch (mode) {
+ case CM_ERASE:
+ fb->cursor.enable = 0;
+ (*fb->setcursor)(fb);
+ fb->hw_cursor_shown = 0;
+ break;
+
+ case CM_MOVE:
+ case CM_DRAW:
+ if (!fb->hw_cursor_shown) {
+ fb->cursor.size.fbx = p->fontwidth;
+ fb->cursor.size.fby = p->fontheight;
+ fb->cursor.chot.fbx = 0;
+ fb->cursor.chot.fby = 0;
+ fb->cursor.enable = 1;
+ memset (fb->cursor.bits, 0, sizeof (fb->cursor.bits));
+ fb->cursor.bits[0][p->fontheight - 2] = (0xffffffff << (32 - p->fontwidth));
+ fb->cursor.bits[1][p->fontheight - 2] = (0xffffffff << (32 - p->fontwidth));
+ fb->cursor.bits[0][p->fontheight - 1] = (0xffffffff << (32 - p->fontwidth));
+ fb->cursor.bits[1][p->fontheight - 1] = (0xffffffff << (32 - p->fontwidth));
+ (*fb->setcursormap) (fb, hw_cursor_cmap, hw_cursor_cmap, hw_cursor_cmap);
+ (*fb->setcurshape) (fb);
+ fb->hw_cursor_shown = 1;
+ }
+ fb->cursor.cpos.fbx = (x << 3) + fb->x_margin; /* x * p->fontwidth */
+ if (p->fontheight == 16)
+ fb->cursor.cpos.fby = (y << 4) + fb->y_margin;
+ else
+ fb->cursor.cpos.fby = (y * p->fontheight) + fb->y_margin;
+ (*fb->setcursor)(fb);
+ break;
+ }
+}
+
+ /*
+ * Get the Colormap
+ */
+
+static int sbusfb_get_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ if (con == currcon) /* current console? */
+ return fb_get_cmap(cmap, &fb_display[con].var, kspc, sbusfb_getcolreg, info);
+ else if (fb_display[con].cmap.len) /* non default colormap? */
+ fb_copy_cmap(&fb_display[con].cmap, cmap, kspc ? 0 : 2);
+ else
+ fb_copy_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel), cmap, kspc ? 0 : 2);
+ return 0;
+}
+
+ /*
+ * Set the Colormap
+ */
+
+static int sbusfb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
+ struct fb_info *info)
+{
+ int err;
+
+ if (!fb_display[con].cmap.len) { /* no colormap allocated? */
+ if ((err = fb_alloc_cmap(&fb_display[con].cmap, 1<<fb_display[con].var.bits_per_pixel, 0)))
+ return err;
+ }
+ if (con == currcon) { /* current console? */
+ err = fb_set_cmap(cmap, &fb_display[con].var, kspc, sbusfb_setcolreg, info);
+ if (!err) {
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (fb->loadcmap)
+ (*fb->loadcmap)(fb, cmap->start, cmap->len);
+ }
+ return err;
+ } else
+ fb_copy_cmap(cmap, &fb_display[con].cmap, kspc ? 0 : 1);
+ return 0;
+}
+
+static int sbusfb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+ u_long arg, int con, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+ int i;
+
+ switch (cmd){
+ case FBIOGTYPE: /* return frame buffer type */
+ copy_to_user_ret((struct fbtype *)arg, &fb->type, sizeof(struct fbtype), -EFAULT);
+ break;
+ case FBIOGATTR: {
+ struct fbgattr *fba = (struct fbgattr *) arg;
+
+ i = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct fbgattr));
+ if (i) return i;
+ __put_user_ret(fb->emulations[0], &fba->real_type, -EFAULT);
+ __put_user_ret(0, &fba->owner, -EFAULT);
+ __copy_to_user_ret(&fba->fbtype, &fb->type,
+ sizeof(struct fbtype), -EFAULT);
+ __put_user_ret(0, &fba->sattr.flags, -EFAULT);
+ __put_user_ret(fb->type.fb_type, &fba->sattr.emu_type, -EFAULT);
+ __put_user_ret(-1, &fba->sattr.dev_specific[0], -EFAULT);
+ for (i = 0; i < 4; i++)
+ put_user_ret(fb->emulations[i], &fba->emu_types[i], -EFAULT);
+ break;
+ }
+ case FBIOSATTR:
+ i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbsattr));
+ if (i) return i;
+ return -EINVAL;
+ case FBIOSVIDEO:
+ if (fb->consolecnt &&
+ vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ break;
+ get_user_ret(i, (int *)arg, -EFAULT);
+ if (i){
+ if (!fb->blanked || !fb->unblank)
+ break;
+ if (fb->consolecnt || (fb->open && fb->mmaped))
+ (*fb->unblank)(fb);
+ fb->blanked = 0;
+ } else {
+ if (fb->blanked || !fb->blank)
+ break;
+ (*fb->blank)(fb);
+ fb->blanked = 1;
+ }
+ break;
+ case FBIOGVIDEO:
+ put_user_ret(fb->blanked, (int *) arg, -EFAULT);
+ break;
+ case FBIOGETCMAP: {
+ char *rp, *gp, *bp;
+ int end, count, index;
+ struct fbcmap *cmap;
+
+ if (!fb->loadcmap)
+ return -EINVAL;
+ i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
+ if (i) return i;
+ cmap = (struct fbcmap *) arg;
+ __get_user_ret(count, &cmap->count, -EFAULT);
+ __get_user_ret(index, &cmap->index, -EFAULT);
+ if ((index < 0) || (index > 255))
+ return -EINVAL;
+ if (index + count > 256)
+ count = 256 - index;
+ __get_user_ret(rp, &cmap->red, -EFAULT);
+ __get_user_ret(gp, &cmap->green, -EFAULT);
+ __get_user_ret(bp, &cmap->blue, -EFAULT);
+ if(verify_area (VERIFY_WRITE, rp, count)) return -EFAULT;
+ if(verify_area (VERIFY_WRITE, gp, count)) return -EFAULT;
+ if(verify_area (VERIFY_WRITE, bp, count)) return -EFAULT;
+ end = index + count;
+ for (i = index; i < end; i++){
+ __put_user_ret(fb->color_map CM(i,0), rp, -EFAULT);
+ __put_user_ret(fb->color_map CM(i,1), gp, -EFAULT);
+ __put_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
+ rp++; gp++; bp++;
+ }
+ (*fb->loadcmap)(fb, index, count);
+ break;
+ }
+ case FBIOPUTCMAP: { /* load color map entries */
+ char *rp, *gp, *bp;
+ int end, count, index;
+ struct fbcmap *cmap;
+
+ if (!fb->loadcmap)
+ return -EINVAL;
+ i = verify_area (VERIFY_READ, (void *) arg, sizeof (struct fbcmap));
+ if (i) return i;
+ cmap = (struct fbcmap *) arg;
+ __get_user_ret(count, &cmap->count, -EFAULT);
+ __get_user_ret(index, &cmap->index, -EFAULT);
+ if ((index < 0) || (index > 255))
+ return -EINVAL;
+ if (index + count > 256)
+ count = 256 - index;
+ __get_user_ret(rp, &cmap->red, -EFAULT);
+ __get_user_ret(gp, &cmap->green, -EFAULT);
+ __get_user_ret(bp, &cmap->blue, -EFAULT);
+ if(verify_area (VERIFY_READ, rp, count)) return -EFAULT;
+ if(verify_area (VERIFY_READ, gp, count)) return -EFAULT;
+ if(verify_area (VERIFY_READ, bp, count)) return -EFAULT;
+
+ end = index + count;
+ for (i = index; i < end; i++){
+ __get_user_ret(fb->color_map CM(i,0), rp, -EFAULT);
+ __get_user_ret(fb->color_map CM(i,1), gp, -EFAULT);
+ __get_user_ret(fb->color_map CM(i,2), bp, -EFAULT);
+ rp++; gp++; bp++;
+ }
+ (*fb->loadcmap)(fb, index, count);
+ break;
+ }
+ case FBIOGCURMAX: {
+ struct fbcurpos *p = (struct fbcurpos *) arg;
+ if (!fb->setcursor) return -EINVAL;
+ if(verify_area (VERIFY_WRITE, p, sizeof (struct fbcurpos)))
+ return -EFAULT;
+ __put_user_ret(fb->cursor.hwsize.fbx, &p->fbx, -EFAULT);
+ __put_user_ret(fb->cursor.hwsize.fby, &p->fby, -EFAULT);
+ break;
+ }
+ case FBIOSCURSOR:
+ if (!fb->setcursor) return -EINVAL;
+ if (fb->consolecnt) {
+ if (vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ return -EINVAL; /* Don't let graphics programs hide our nice text cursor */
+ fb->hw_cursor_shown = 0; /* Forget state of our text cursor */
+ }
+ return sbus_hw_scursor ((struct fbcursor *) arg, fb);
+
+ case FBIOSCURPOS:
+ if (!fb->setcursor) return -EINVAL;
+ /* Don't let graphics programs move our nice text cursor */
+ if (fb->consolecnt) {
+ if (vt_cons[fb->lastconsole]->vc_mode == KD_TEXT)
+ return -EINVAL; /* Don't let graphics programs move our nice text cursor */
+ }
+ if (copy_from_user(&fb->cursor.cpos, (void *)arg, sizeof(struct fbcurpos)))
+ return -EFAULT;
+ (*fb->setcursor) (fb);
+ break;
+ default:
+ /* FIXME: Call here possible fb specific ioctl */
+ return -EINVAL;
+ }
+ return 0;
+}
+
+ /*
+ * Setup: parse used options
+ */
+
+void sbusfb_setup(char *options, int *ints)
+{
+ if (!strncmp(options, "nomargins", 9))
+ nomargins = 1;
+}
+
+static int sbusfbcon_switch(int con, struct fb_info *info)
+{
+ /* Do we have to save the colormap? */
+ if (fb_display[currcon].cmap.len)
+ fb_get_cmap(&fb_display[currcon].cmap, &fb_display[currcon].var, 1, sbusfb_getcolreg, info);
+
+ sbusfbinfo(info)->lastconsole = con;
+ currcon = con;
+ /* Install new colormap */
+ do_install_cmap(con, info);
+ return 0;
+}
+
+ /*
+ * Update the `var' structure (called by fbcon.c)
+ */
+
+static int sbusfbcon_updatevar(int con, struct fb_info *info)
+{
+ /* Nothing */
+ return 0;
+}
+
+ /*
+ * Blank the display.
+ */
+
+static void sbusfbcon_blank(int blank, struct fb_info *info)
+{
+#if 0
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+ int i, j;
+
+ if (!fb->cmap_adr)
+ return;
+
+ if (blank)
+ for (i = 0; i < 256; i++) {
+ *fb->cmap_adr = i;
+ for (j = 0; j < 3; j++) {
+ *fb->cmap_data = 0;
+ }
+ }
+ else
+ do_install_cmap(currcon, info);
+#endif
+}
+
+ /*
+ * Read a single color register and split it into
+ * colors/transparent. Return != 0 for invalid regno.
+ */
+
+static int sbusfb_getcolreg(u_int regno, u_int *red, u_int *green, u_int *blue,
+ u_int *transp, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (!fb->color_map || regno > 255)
+ return 1;
+ *red = fb->color_map CM(regno, 0);
+ *green = fb->color_map CM(regno, 1);
+ *blue = fb->color_map CM(regno, 2);
+ return 0;
+}
+
+
+ /*
+ * Set a single color register. The values supplied are already
+ * rounded down to the hardware's capabilities (according to the
+ * entries in the var structure). Return != 0 for invalid regno.
+ */
+
+static int sbusfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
+ u_int transp, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (!fb->color_map || regno > 255)
+ return 1;
+ fb->color_map CM(regno, 0) = red;
+ fb->color_map CM(regno, 1) = green;
+ fb->color_map CM(regno, 2) = blue;
+ return 0;
+}
+
+
+static void do_install_cmap(int con, struct fb_info *info)
+{
+ struct fb_info_sbusfb *fb = sbusfbinfo(info);
+
+ if (con != currcon)
+ return;
+ if (fb_display[con].cmap.len)
+ fb_set_cmap(&fb_display[con].cmap, &fb_display[con].var, 1,
+ sbusfb_setcolreg, info);
+ else
+ fb_set_cmap(fb_default_cmap(1<<fb_display[con].var.bits_per_pixel),
+ &fb_display[con].var, 1, sbusfb_setcolreg, info);
+ if (fb->loadcmap)
+ (*fb->loadcmap)(fb, 0, 256);
+}
+
+ /*
+ * Initialisation
+ */
+
+__initfunc(static void sbusfb_init_fb(int node, int parent, int fbtype,
+ struct linux_sbus_device *sbdp))
+{
+ struct fb_fix_screeninfo *fix;
+ struct fb_var_screeninfo *var;
+ struct display *disp;
+ struct fb_info_sbusfb *fb;
+ struct fbtype *type;
+ int linebytes, w, h, depth;
+ char *p = NULL;
+ int margin;
+
+ fb = kmalloc(sizeof(struct fb_info_sbusfb), GFP_ATOMIC);
+ if (!fb) {
+ prom_printf("Could not allocate sbusfb structure\n");
+ return;
+ }
+ memset(fb, 0, sizeof(struct fb_info_sbusfb));
+ fix = &fb->fix;
+ var = &fb->var;
+ disp = &fb->disp;
+ type = &fb->type;
+
+ fb->prom_node = node;
+ fb->prom_parent = parent;
+ fb->sbdp = sbdp;
+ if (sbdp)
+ fb->iospace = sbdp->reg_addrs[0].which_io;
+
+ type->fb_type = fbtype;
+ memset(&fb->emulations, 0xff, sizeof(fb->emulations));
+ fb->emulations[0] = fbtype;
+
+#ifndef __sparc_v9__
+ disp->screen_base = prom_getintdefault(node, "address", 0);
+#endif
+
+ type->fb_height = h = prom_getintdefault(node, "height", 900);
+ type->fb_width = w = prom_getintdefault(node, "width", 1152);
+ type->fb_depth = depth = (fbtype == FBTYPE_SUN2BW) ? 1 : 8;
+ linebytes = prom_getintdefault(node, "linebytes", w * depth / 8);
+ type->fb_size = PAGE_ALIGN((linebytes) * h);
+
+ if (!nomargins)
+ for (margin = 0; def_margins[margin].depth; margin++)
+ if (w == def_margins[margin].xres &&
+ h == def_margins[margin].yres &&
+ depth == def_margins[margin].depth) {
+ fb->x_margin = def_margins[margin].x_margin;
+ fb->y_margin = def_margins[margin].y_margin;
+ break;
+ }
+ fb->x_margin += ((w - 2*fb->x_margin) & 15) / 2;
+ fb->y_margin += ((h - 2*fb->y_margin) & 15) / 2;
+
+ var->xres_virtual = w;
+ var->yres_virtual = h;
+ var->xres = w - 2*fb->x_margin;
+ var->yres = h - 2*fb->y_margin;
+
+ var->bits_per_pixel = depth;
+ var->height = var->width = -1;
+ var->pixclock = 10000;
+ var->vmode = FB_VMODE_NONINTERLACED;
+ var->red.length = var->green.length = var->blue.length = 8;
+
+ fix->line_length = linebytes;
+ fix->smem_len = type->fb_size;
+ fix->type = FB_TYPE_PACKED_PIXELS;
+ fix->visual = FB_VISUAL_PSEUDOCOLOR;
+
+ fb->info.node = -1;
+ fb->info.fbops = &sbusfb_ops;
+ fb->info.disp = disp;
+ fb->info.fontname[0] = '\0';
+ fb->info.changevar = NULL;
+ fb->info.switch_con = &sbusfbcon_switch;
+ fb->info.updatevar = &sbusfbcon_updatevar;
+ fb->info.blank = &sbusfbcon_blank;
+
+ fb->cursor.hwsize.fbx = 32;
+ fb->cursor.hwsize.fby = 32;
+
+ if (depth > 1)
+ fb->color_map = kmalloc(256 * 3, GFP_ATOMIC);
+
+ switch(fbtype) {
+#ifdef CONFIG_FB_CREATOR
+ case FBTYPE_CREATOR:
+ p = creatorfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGSIX
+ case FBTYPE_SUNFAST_COLOR:
+ p = cgsixfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGTHREE
+ case FBTYPE_SUN3COLOR:
+ p = cgthreefb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_TCX
+ case FBTYPE_TCXCOLOR:
+ p = tcxfb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_LEO
+ case FBTYPE_SUNLEO:
+ p = leofb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_BWTWO
+ case FBTYPE_SUN2BW:
+ p = bwtwofb_init(fb); break;
+#endif
+#ifdef CONFIG_FB_CGFOURTEEN
+ case FBTYPE_MDICOLOR:
+ p = cgfourteenfb_init(fb); break;
+#endif
+ }
+
+ if (!p) {
+ kfree(fb);
+ return;
+ }
+
+ disp->dispsw = &fb->dispsw;
+ if (fb->setcursor)
+ fb->dispsw.cursor = sbusfb_cursor;
+ fb->setup = fb->dispsw.setup;
+ fb->dispsw.setup = sbusfb_disp_setup;
+
+ disp->var = *var;
+ disp->visual = fix->visual;
+ disp->type = fix->type;
+ disp->type_aux = fix->type_aux;
+ disp->line_length = fix->line_length;
+
+ sbusfb_set_var(var, -1, &fb->info);
+
+ if (register_framebuffer(&fb->info) < 0) {
+ kfree(fb);
+ return;
+ }
+ printk("fb%d: %s\n", GET_FB_IDX(fb->info.node), p);
+}
+
+static inline int known_card(char *name)
+{
+ char *p;
+ for (p = name; *p && *p != ','; p++);
+ if (*p == ',') name = p + 1;
+ if (!strcmp(p, "cgsix") || !strcmp(p, "cgthree+"))
+ return FBTYPE_SUNFAST_COLOR;
+ if (!strcmp(p, "cgthree") || !strcmp(p, "cgRDI"))
+ return FBTYPE_SUN3COLOR;
+ if (!strcmp(p, "cgfourteen"))
+ return FBTYPE_MDICOLOR;
+ if (!strcmp(p, "leo"))
+ return FBTYPE_SUNLEO;
+ if (!strcmp(p, "bwtwo"))
+ return FBTYPE_SUN2BW;
+ if (!strcmp(p, "tcx"))
+ return FBTYPE_TCXCOLOR;
+ return FBTYPE_NOTYPE;
+}
+
+__initfunc(void sbusfb_init(void))
+{
+ int node, root, type;
+ struct linux_sbus_device *sbdp;
+ struct linux_sbus *sbus;
+ char prom_name[40];
+
+#ifdef CONFIG_FB_CREATOR
+ root = prom_getchild(prom_root_node);
+ for (node = prom_searchsiblings(root, "SUNW,ffb"); node;
+ node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) {
+ sbusfb_init_fb(node, prom_root_node, FBTYPE_CREATOR, NULL);
+ }
+#endif
+#ifdef CONFIG_SUN4
+ sbusfb_init_fb(0, 0, FBTYPE_SUN2BW, NULL);
+#endif
+#if defined(CONFIG_FB_CGFOURTEEN) && !defined(__sparc_v9__)
+ root = prom_getchild(prom_root_node);
+ root = prom_searchsiblings(root, "obio");
+ if (root &&
+ (node = prom_searchsiblings(prom_getchild(root), "cgfourteen"))) {
+ sbusfb_init_fb(node, root, FBTYPE_MDICOLOR, NULL);
+ }
+#endif
+ if (!SBus_chain) return;
+ for_all_sbusdev(sbdp, sbus) {
+ type = known_card(sbdp->prom_name);
+ if (type == FBTYPE_NOTYPE) continue;
+ if (prom_getproperty(sbdp->prom_node, "emulation", prom_name, sizeof(prom_name)) > 0) {
+ type = known_card(prom_name);
+ if (type == FBTYPE_NOTYPE) type = known_card(sbdp->prom_name);
+ }
+ prom_apply_sbus_ranges(sbdp->my_bus, &sbdp->reg_addrs[0],
+ sbdp->num_registers, sbdp);
+ sbusfb_init_fb(sbdp->prom_node, sbdp->my_bus->prom_node, type, sbdp);
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov