patch-2.4.19 linux-2.4.19/drivers/media/video/planb.c

Next file: linux-2.4.19/drivers/media/video/planb.h
Previous file: linux-2.4.19/drivers/media/video/msp3400.c
Back to the patch index
Back to the overall index

diff -urN linux-2.4.18/drivers/media/video/planb.c linux-2.4.19/drivers/media/video/planb.c
@@ -1,14 +1,17 @@
 /* 
-    planb - PlanB frame grabber driver
+    planb - v4l-compatible frame grabber driver for the PlanB hardware
 
     PlanB is used in the 7x00/8x00 series of PowerMacintosh
     Computers as video input DMA controller.
 
-    Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+    Copyright (C) 1998 - 2002  Michel Lanners <mailto:mlan@cpu.lu>
 
-    Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
+    Based largely on the old bttv driver by Ralph Metzler
+
+    Additional debugging and coding by Takashi Oe <mailto:toe@unlserve.unl.edu>
+
+    For more information, see <http://www.cpu.lu/~mlan/planb.html>
 
-    Additional debugging and coding by Takashi Oe (toe@unlserve.unl.edu)
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
@@ -25,7 +28,7 @@
     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */
 
-/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */
+/* $Id: planb.c,v 2.11 2002/04/03 15:57:57 mlan Exp mlan $ */
 
 #include <linux/version.h>
 #include <linux/init.h>
@@ -40,6 +43,7 @@
 #include <linux/vmalloc.h>
 #include <linux/mm.h>
 #include <linux/sched.h>
+#include <linux/poll.h>
 #include <linux/wrapper.h>
 #include <linux/tqueue.h>
 #include <linux/videodev.h>
@@ -52,37 +56,41 @@
 #include <asm/irq.h>
 #include <asm/semaphore.h>
 
-#include "planb.h"
-#include "saa7196.h"
+/* Define these to get general / interrupt debugging */
+#undef DEBUG
+#undef IDEBUG
 
-/* Would you mind for some ugly debugging? */
-#if 0
-#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */
+//#define DEBUG
+
+#ifdef DEBUG
+#define DBG(x...) printk(KERN_DEBUG ## x)
 #else
-#define DEBUG(x...) 		/* Don't debug driver */
+#define DBG(x...)
 #endif
-
-#if 0
-#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */
+#ifdef IDEBUG
+#define IDBG(x...) printk(KERN_DEBUG ## x)
 #else
-#define IDEBUG(x...) 		/* Don't debug interrupt part */
+#define IDBG(x...)
 #endif
 
-/* Ever seen a Mac with more than 1 of these? */
-#define PLANB_MAX 1
+#include "planb.h"
+#include "saa7196.h"
 
-static int planb_num;
-static struct planb planbs[PLANB_MAX];
+static struct planb planbs;
 static volatile struct planb_registers *planb_regs;
 
 static int def_norm = PLANB_DEF_NORM;	/* default norm */
 static int video_nr = -1;
+static int vbi_nr = -1;
 
 MODULE_PARM(def_norm, "i");
 MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");
 MODULE_PARM(video_nr,"i");
-MODULE_LICENSE("GPL");
+MODULE_PARM(vbi_nr,"i");
 
+MODULE_DESCRIPTION("planb - v4l driver module for Apple PlanB video in");
+MODULE_AUTHOR("Michel Lanners & Takashi Oe - see: http://www.cpu.lu/planb.html");
+MODULE_LICENSE("GPL");
 
 /* ------------------ PlanB Exported Functions ------------------ */
 static long planb_write(struct video_device *, const char *, unsigned long, int);
@@ -92,12 +100,23 @@
 static int planb_ioctl(struct video_device *, unsigned int, void *);
 static int planb_mmap(struct video_device *, const char *, unsigned long);
 static void planb_irq(int, void *, struct pt_regs *);
+static int planb_vbi_open(struct video_device *, int);
+static void planb_vbi_close(struct video_device *);
+static long planb_vbi_read(struct video_device *, char *, unsigned long, int);
+static unsigned int planb_vbi_poll(struct video_device *, struct file *,
+	poll_table *);
+static int planb_vbi_ioctl(struct video_device *, unsigned int, void *);
 static void release_planb(void);
-static int init_planbs(void);
+static int __init init_planbs(void);
+static void __exit exit_planbs(void);
 
 /* ------------------ PlanB Internal Functions ------------------ */
 static int planb_prepare_open(struct planb *);
+static int planb_prepare_vbi(struct planb *);
+static int planb_prepare_video(struct planb *);
 static void planb_prepare_close(struct planb *);
+static void planb_close_vbi(struct planb *);
+static void planb_close_video(struct planb *);
 static void saa_write_reg(unsigned char, unsigned char);
 static unsigned char saa_status(int, struct planb *);
 static void saa_set(unsigned char, unsigned char, struct planb *);
@@ -107,28 +126,41 @@
 static void add_clip(struct planb *, struct video_clip *);
 static void fill_cmd_buff(struct planb *);
 static void cmd_buff(struct planb *);
-static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *);
+static dbdma_cmd_ptr setup_grab_cmd(int, struct planb *);
 static void overlay_start(struct planb *);
 static void overlay_stop(struct planb *);
-static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short,
-	unsigned int);
-static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int,
-	unsigned int);
-static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short,
-	unsigned short, unsigned int, unsigned int);
+static inline void tab_cmd_dbdma(dbdma_cmd_ptr, unsigned short, unsigned int);
+static inline void tab_cmd_store(dbdma_cmd_ptr, unsigned int, unsigned int);
+static inline void tab_cmd_gen(dbdma_cmd_ptr, unsigned short, unsigned short,
+	unsigned int, unsigned int);
 static int init_planb(struct planb *);
 static int find_planb(void);
-static void planb_pre_capture(int, int, struct planb *);
-static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *,
-					int, int, int, int, int, struct planb *);
-static inline void planb_dbdma_stop(volatile struct dbdma_regs *);
-static unsigned int saa_geo_setup(int, int, int, int, struct planb *);
+static void planb_pre_capture(int, struct planb *);
+static dbdma_cmd_ptr cmd_geo_setup(dbdma_cmd_ptr, int, int, int, int, int,
+	struct planb *);
+static inline void planb_dbdma_stop(dbdma_regs_ptr);
+static inline void planb_dbdma_restart(dbdma_regs_ptr);
+static void saa_geo_setup(int, int, int, int, struct planb *);
 static inline int overlay_is_active(struct planb *);
 
 /*******************************/
 /* Memory management functions */
 /*******************************/
 
+/* I know this is not the right way to allocate memory. Whoever knows
+ * the right way to allocate a huge buffer for DMA that can be mapped
+ * to user space, please tell me... or better, fix the code and send
+ * patches.
+ *
+ *					Michel Lanners (mlan@cpu.lu)
+ */
+/* FIXME: As subsequent calls to __get_free_pages don't necessarily return
+ * contiguous pages, we need to do horrible things later on when setting
+ * up DMA, to make sure a single DMA transfer doesn't cross a page boundary.
+ * At least, I hope it's done right later on ;-) ......
+ * Anyway, there should be a way to get hold of a large buffer of contiguous
+ * pages for DMA....
+ */
 static int grabbuf_alloc(struct planb *pb)
 {
 	int i, npage;
@@ -142,14 +174,14 @@
 				* sizeof(unsigned long), GFP_KERNEL)) == 0)
 		return -ENOMEM;
 	for (i = 0; i < npage; i++) {
-		pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL
-								|GFP_DMA, 0);
+		pb->rawbuf[i] = (unsigned char *)__get_free_pages(GFP_KERNEL |
+								GFP_DMA, 0);
 		if (!pb->rawbuf[i])
 			break;
 		mem_map_reserve(virt_to_page(pb->rawbuf[i]));
 	}
 	if (i-- < npage) {
-		printk(KERN_DEBUG "PlanB: init_grab: grab buffer not allocated\n");
+		DBG("PlanB: init_grab: grab buffer not allocated\n");
 		for (; i > 0; i--) {
 			mem_map_unreserve(virt_to_page(pb->rawbuf[i]));
 			free_pages((unsigned long)pb->rawbuf[i], 0);
@@ -157,7 +189,7 @@
 		kfree(pb->rawbuf);
 		return -ENOBUFS;
 	}
-	pb->rawbuf_size = npage;
+	pb->rawbuf_nchunks = npage;
 	return 0;
 }
 
@@ -181,12 +213,7 @@
 
 	/* Let's wait 30msec for this one */
 	current->state = TASK_INTERRUPTIBLE;
-#if LINUX_VERSION_CODE >= 0x02017F
 	schedule_timeout(30 * HZ / 1000);
-#else
-	current->timeout = jiffies + 30 * HZ / 1000;	/* 30 ms */;
-	schedule();
-#endif
 
 	return (unsigned char)in_8 (&planb_regs->saa_status);
 }
@@ -208,25 +235,15 @@
 		saa_write_reg (i, saa_regs[pb->win.norm][i]);
 }
 
-static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp,
-	struct planb *pb)
+static void saa_geo_setup(int width, int height, int interlace,
+	int fmt, struct planb *pb)
 {
 	int ht, norm = pb->win.norm;
 
-	switch(bpp) {
-	case 2:
-		/* RGB555+a 1x16-bit + 16-bit transparent */
-		saa_regs[norm][SAA7196_FMTS] &= ~0x3;
-		break;
-	case 1:
-	case 4:
-		/* RGB888 1x24-bit + 8-bit transparent */
-		saa_regs[norm][SAA7196_FMTS] &= ~0x1;
-		saa_regs[norm][SAA7196_FMTS] |= 0x2;
-		break;
-	default:
-		return -EINVAL;
-	}
+	/* bits FS0, FS1 according to format spec */
+	saa_regs[norm][SAA7196_FMTS] &= ~0x3;
+	saa_regs[norm][SAA7196_FMTS] |= (palette2fmt[fmt].saa_fmt & 0x3);
+
 	ht = (interlace ? height / 2 : height);
 	saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff);
 	saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3)
@@ -240,93 +257,99 @@
 					: (saa_regs[norm][SAA7196_FMTS] | 0x60);
 	/* transparent mode; extended format enabled */
 	saa_regs[norm][SAA7196_DPATH] |= 0x3;
-
-	return 0;
+	/* bits LLV, MCT according to format spec */
+	saa_regs[norm][SAA7196_DPATH] &= ~0x30;
+	saa_regs[norm][SAA7196_DPATH] |= (palette2fmt[fmt].saa_fmt & 0x30);
 }
 
 /***************************/
 /* DBDMA support functions */
 /***************************/
 
-static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch)
+static inline void planb_dbdma_restart(dbdma_regs_ptr ch)
 {
-	out_le32(&ch->control, PLANB_CLR(RUN));
-	out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE));
+	writel(PLANB_CLR(RUN), &ch->control);
+	writel(PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE), &ch->control);
 }
 
-static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch)
+static inline void planb_dbdma_stop(dbdma_regs_ptr ch)
 {
 	int i = 0;
 
-	out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH));
-	while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {
-		IDEBUG("PlanB: waiting for DMA to stop\n");
+	writel(PLANB_CLR(RUN) | PLANB_SET(FLUSH), &ch->control);
+	while((readl(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {
+		IDBG("PlanB: waiting for DMA to stop\n");
 		i++;
 	}
 }
 
-static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch,
-	unsigned short command, unsigned int cmd_dep)
+static inline void tab_cmd_dbdma(dbdma_cmd_ptr ch, unsigned short command,
+	unsigned int cmd_dep)
 {
 	st_le16(&ch->command, command);
+	st_le16(&ch->req_count, 0);
+	st_le32(&ch->phy_addr, 0);
 	st_le32(&ch->cmd_dep, cmd_dep);
+	/* really clears res_count & xfer_status */
+	st_le32((unsigned int *)&ch->res_count, 0);
 }
 
-static inline void tab_cmd_store(volatile struct dbdma_cmd *ch,
-	unsigned int phy_addr, unsigned int cmd_dep)
+static inline void tab_cmd_store(dbdma_cmd_ptr ch, unsigned int phy_addr,
+	unsigned int cmd_dep)
 {
 	st_le16(&ch->command, STORE_WORD | KEY_SYSTEM);
 	st_le16(&ch->req_count, 4);
 	st_le32(&ch->phy_addr, phy_addr);
 	st_le32(&ch->cmd_dep, cmd_dep);
+	st_le32((unsigned int *)&ch->res_count, 0);
 }
 
-static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch,
-	unsigned short command, unsigned short req_count,
-	unsigned int phy_addr, unsigned int cmd_dep)
+static inline void tab_cmd_gen(dbdma_cmd_ptr ch, unsigned short command,
+	unsigned short req_count, unsigned int phy_addr, unsigned int cmd_dep)
 {
 	st_le16(&ch->command, command);
 	st_le16(&ch->req_count, req_count);
 	st_le32(&ch->phy_addr, phy_addr);
 	st_le32(&ch->cmd_dep, cmd_dep);
+	st_le32((unsigned int *)&ch->res_count, 0);
 }
 
-static volatile struct dbdma_cmd *cmd_geo_setup(
-	volatile struct dbdma_cmd *c1, int width, int height, int interlace,
-	int bpp, int clip, struct planb *pb)
+static dbdma_cmd_ptr cmd_geo_setup(dbdma_cmd_ptr c1, int width, int height,
+	int interlace, int fmt, int clip, struct planb *pb)
 {
 	int norm = pb->win.norm;
 
-	if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0)
-		return (volatile struct dbdma_cmd *)NULL;
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
-							SAA7196_FMTS);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
-					saa_regs[norm][SAA7196_FMTS]);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
-							SAA7196_DPATH);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
-					saa_regs[norm][SAA7196_DPATH]);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even),
-					bpp | ((clip)? PLANB_CLIPMASK: 0));
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd),
-					bpp | ((clip)? PLANB_CLIPMASK: 0));
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
-							SAA7196_OUTPIX);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
-					saa_regs[norm][SAA7196_OUTPIX]);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
-							SAA7196_HFILT);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
-					saa_regs[norm][SAA7196_HFILT]);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+	saa_geo_setup(width, height, interlace, fmt, pb);
+	/* if the number of DBDMA commands here (14) changes, lots of
+	 * things need to be corrected accordingly... */
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
+								SAA7196_FMTS);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
+						saa_regs[norm][SAA7196_FMTS]);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
+								SAA7196_DPATH);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
+						saa_regs[norm][SAA7196_DPATH]);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->even),
+			palette2fmt[fmt].pb_fmt | ((clip)? PLANB_CLIPMASK: 0));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->odd),
+			palette2fmt[fmt].pb_fmt | ((clip)? PLANB_CLIPMASK: 0));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
+								SAA7196_OUTPIX);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
+						saa_regs[norm][SAA7196_OUTPIX]);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
+								SAA7196_HFILT);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
+						saa_regs[norm][SAA7196_HFILT]);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
 							SAA7196_OUTLINE);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
 					saa_regs[norm][SAA7196_OUTLINE]);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
-							SAA7196_VYP);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
-					saa_regs[norm][SAA7196_VYP]);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_addr),
+								SAA7196_VYP);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->saa_regval),
+						saa_regs[norm][SAA7196_VYP]);
 	return c1;
 }
 
@@ -336,11 +359,13 @@
 
 static inline void planb_lock(struct planb *pb)
 {
+	DBG("PlanB: planb_lock\n");
 	down(&pb->lock);
 }
 
 static inline void planb_unlock(struct planb *pb)
 {
+	DBG("PlanB: planb_unlock\n");
 	up(&pb->lock);
 }
 
@@ -348,47 +373,116 @@
 /* Driver Core */
 /***************/
 
+/* number of entries in the circular DBDMA command buffer
+ * initial stop, odd/even vbi, odd/even video, branch back */
+#define NUMJUMPS 6
 static int planb_prepare_open(struct planb *pb)
 {
+	dbdma_cmd_ptr	c;
+	int		size, i;
+
+	size = (NUMJUMPS + 1) * sizeof(struct dbdma_cmd);
+
+	if((pb->jump_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)
+		return -ENOMEM;
+	memset(pb->jump_raw, 0, size);
+	c = pb->jumpbuf = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->jump_raw);
+
+	/* circular DBDMA command buffer, to hold jumps to transfer commands */
+	tab_cmd_dbdma(c++, DBDMA_STOP, 0);
+	for (i=1; i<NUMJUMPS-1; i++)
+		tab_cmd_dbdma(c++, DBDMA_NOP, 0);
+	tab_cmd_dbdma(c, DBDMA_NOP|BR_ALWAYS,
+		(unsigned int)pb->jumpbuf);
+
+	DBG("PlanB: planb_prepare_open, jumpbuffer at 0x%08x, length %d.\n",
+		(unsigned int)pb->jumpbuf, size); 
+	return 0;
+}
+
+#define VBIDUMMY 40	/* must be even !! */
+static int planb_prepare_vbi(struct planb *pb)
+{
+	int	size;
+
+	/* allocate VBI comand buffer memory
+	   (2 fields * VBI_MAXLINES + 40 handling + alignment) */
+	size = (2*VBI_MAXLINES + VBIDUMMY + 1) * sizeof(struct dbdma_cmd);
+
+	if ((pb->vbi_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)
+		return -ENOMEM;
+	memset (pb->vbi_raw, 0, size);
+	size = (VBI_MAXLINES + VBIDUMMY/2) * sizeof(struct dbdma_cmd);
+	pb->vbi_cbo.start = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->vbi_raw);
+	pb->vbi_cbo.size = pb->vbi_cbe.size = size;
+	pb->vbi_cbe.start = pb->vbi_cbo.start + pb->vbi_cbo.size;
+	pb->vbi_cbo.jumpaddr = pb->jumpbuf + 1;
+	pb->vbi_cbe.jumpaddr = pb->jumpbuf + 3;
+
+	DBG("PlanB: planb_prepare_vbi, dbdma cmd_buf at 0x%08x, length %d.\n",
+		(unsigned int)pb->vbi_cbo.start, 2*size); 
+	return 0;
+}
+
+static int planb_prepare_video(struct planb *pb)
+{
 	int	i, size;
 
+	/* FIXME: This is stressing kmalloc to its limits...
+		  We really should allocate smaller chunks. */
+
 	/* allocate memory for two plus alpha command buffers (size: max lines,
 	   plus 40 commands handling, plus 1 alignment), plus dummy command buf,
 	   plus clipmask buffer, plus frame grabbing status */
-	size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS
-		* PLANB_DUMMY)*sizeof(struct dbdma_cmd)
-		+(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8
-		+MAX_GBUFFERS*sizeof(unsigned int);
-	if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0)
+	size = (pb->tab_size * (2 + MAX_GBUFFERS * TAB_FACTOR)
+		    + MAX_GBUFFERS * PLANB_DUMMY + 1) * sizeof(struct dbdma_cmd)
+		+ (PLANB_MAXLINES * ((PLANB_MAXPIXELS + 7) & ~7)) / 8
+		+ MAX_GBUFFERS * sizeof(unsigned int);
+	if ((pb->vid_raw = kmalloc (size, GFP_KERNEL|GFP_DMA)) == 0)
 		return -ENOMEM;
-	memset ((void *) pb->priv_space, 0, size);
-	pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *)
-						DBDMA_ALIGN (pb->priv_space);
-	pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size;
-	pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd);
-	pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size;
-	pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR;
+	memset (pb->vid_raw, 0, size);
+	pb->vid_cbo.start = (dbdma_cmd_ptr) DBDMA_ALIGN (pb->vid_raw);
+	pb->vid_cbo.size = pb->vid_cbe.size = pb->tab_size/2;
+	pb->vid_cbe.start = pb->vid_cbo.start + pb->vid_cbo.size;
+	pb->vid_cbo.jumpaddr = pb->jumpbuf + 2;
+	pb->vid_cbe.jumpaddr = pb->jumpbuf + 4;
+	pb->overlay_last1 = pb->vid_cbo.start;
+	pb->vid_cbo.bus = virt_to_bus(pb->vid_cbo.start);
+	pb->vid_cbe.bus = virt_to_bus(pb->vid_cbe.start);
+	pb->clip_cbo.start = pb->vid_cbe.start + pb->vid_cbe.size;
+	pb->clip_cbo.size = pb->clip_cbe.size = pb->tab_size/2;
+	pb->clip_cbe.start = pb->clip_cbo.start + pb->clip_cbo.size;
+	pb->overlay_last2 = pb->clip_cbo.start;
+	pb->clip_cbo.bus = virt_to_bus(pb->clip_cbo.start);
+	pb->clip_cbe.bus = virt_to_bus(pb->clip_cbe.start);
+	pb->gbuf[0].cap_cmd = pb->clip_cbe.start + pb->clip_cbe.size;
+	pb->gbuf[0].pre_cmd = pb->gbuf[0].cap_cmd + pb->tab_size * TAB_FACTOR;
 	for (i = 1; i < MAX_GBUFFERS; i++) {
-		pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY;
-		pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR;
-	}
-	pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1]
-						+ PLANB_DUMMY);
-	pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
+		pb->gbuf[i].cap_cmd = pb->gbuf[i-1].pre_cmd + PLANB_DUMMY;
+		pb->gbuf[i].pre_cmd = pb->gbuf[i].cap_cmd +
+						pb->tab_size * TAB_FACTOR;
+	}
+	pb->gbuf[0].status = (volatile unsigned int *)
+			(pb->gbuf[MAX_GBUFFERS-1].pre_cmd + PLANB_DUMMY);
+	for (i = 1; i < MAX_GBUFFERS; i++)
+		pb->gbuf[i].status = pb->gbuf[i-1].status;
+	pb->mask = (unsigned char *)(pb->gbuf[MAX_GBUFFERS-1].status + 1);
 
 	pb->rawbuf = NULL;
-	pb->rawbuf_size = 0;
+	pb->rawbuf_nchunks = 0;
 	pb->grabbing = 0;
 	for (i = 0; i < MAX_GBUFFERS; i++) {
-		pb->frame_stat[i] = GBUFFER_UNUSED;
-		pb->gwidth[i] = 0;
-		pb->gheight[i] = 0;
-		pb->gfmt[i] = 0;
-		pb->gnorm_switch[i] = 0;
+		gbuf_ptr	gbuf = &pb->gbuf[i];
+
+		*gbuf->status = GBUFFER_UNUSED;
+		gbuf->width = 0;
+		gbuf->height = 0;
+		gbuf->fmt = 0;
+		gbuf->norm_switch = 0;
 #ifndef PLANB_GSCANLINE
-		pb->lsize[i] = 0;
-		pb->lnum[i] = 0;
-#endif /* PLANB_GSCANLINE */
+		gbuf->lsize = 0;
+		gbuf->lnum = 0;
+#endif
 	}
 	pb->gcount = 0;
 	pb->suspend = 0;
@@ -399,30 +493,74 @@
 	planb_dbdma_stop(&pb->planb_base->ch2);
 	planb_dbdma_stop(&pb->planb_base->ch1);
 
+	DBG("PlanB: planb_prepare_video, dbdma cmd_buf at 0x%08x, "
+		"length %d.\n", (unsigned int)pb->vid_cbo.start, 2*size);
 	return 0;
 }
 
 static void planb_prepare_close(struct planb *pb)
 {
-	int i;
-
 	/* make sure the dma's are idle */
 	planb_dbdma_stop(&pb->planb_base->ch2);
 	planb_dbdma_stop(&pb->planb_base->ch1);
-	/* free kernel memory of command buffers */
-	if(pb->priv_space != 0) {
-		kfree (pb->priv_space);
-		pb->priv_space = 0;
+
+	if(pb->jump_raw != 0) {
+		kfree(pb->jump_raw);
+		pb->jump_raw = 0;
+	}
+	return;
+}
+
+static void planb_close_vbi(struct planb *pb)
+{
+	/* FIXME: stop running DMA */
+
+	/* Make sure the DMA controller doesn't jump here anymore */
+	tab_cmd_dbdma(pb->vbi_cbo.jumpaddr, DBDMA_NOP, 0);
+	tab_cmd_dbdma(pb->vbi_cbe.jumpaddr, DBDMA_NOP, 0);
+
+	if(pb->vbi_raw != 0) {
+		kfree (pb->vbi_raw);
+		pb->vbi_raw = 0;
+	}
+
+	/* FIXME: deallocate VBI data buffer */
+
+	/* FIXME: restart running DMA if app. */
+	return;
+}
+
+static void planb_close_video(struct planb *pb)
+{
+	int i;
+
+	/* FIXME: stop running DMA */
+
+	/* Make sure the DMA controller doesn't jump here anymore */
+	tab_cmd_dbdma(pb->vid_cbo.jumpaddr, DBDMA_NOP, 0);
+	tab_cmd_dbdma(pb->vid_cbe.jumpaddr, DBDMA_NOP, 0);
+/* No clipmask jumpbuffer yet  */
+#if 0
+	tab_cmd_dbdma(pb->clip_cbo.jumpaddr, DBDMA_NOP, 0);
+	tab_cmd_dbdma(pb->clip_cbe.jumpaddr, DBDMA_NOP, 0);
+#endif
+
+	if(pb->vid_raw != 0) {
+		kfree (pb->vid_raw);
+		pb->vid_raw = 0;
 		pb->cmd_buff_inited = 0;
 	}
 	if(pb->rawbuf) {
-		for (i = 0; i < pb->rawbuf_size; i++) {
+		for (i = 0; i < pb->rawbuf_nchunks; i++) {
 			mem_map_unreserve(virt_to_page(pb->rawbuf[i]));
 			free_pages((unsigned long)pb->rawbuf[i], 0);
 		}
 		kfree(pb->rawbuf);
 	}
 	pb->rawbuf = NULL;
+
+	/* FIXME: restart running DMA if app. */
+	return;
 }
 
 /*****************************/
@@ -431,43 +569,37 @@
 
 static void overlay_start(struct planb *pb)
 {
+	DBG("PlanB: overlay_start()\n");
 
-	DEBUG("PlanB: overlay_start()\n");
-
-	if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+	if(ACTIVE & readl(&pb->planb_base->ch1.status)) {
 
-		DEBUG("PlanB: presumably, grabbing is in progress...\n");
+		DBG("PlanB: presumably, grabbing is in progress...\n");
 
 		planb_dbdma_stop(&pb->planb_base->ch2);
-		out_le32 (&pb->planb_base->ch2.cmdptr,
-						virt_to_bus(pb->ch2_cmd));
+		writel(pb->clip_cbo.bus, &pb->planb_base->ch2.cmdptr);
 		planb_dbdma_restart(&pb->planb_base->ch2);
-		st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
-		tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
-					DBDMA_NOP | BR_ALWAYS,
-					virt_to_bus(pb->ch1_cmd));
+		st_le16 (&pb->vid_cbo.start->command, DBDMA_NOP);
+		tab_cmd_dbdma(pb->gbuf[pb->last_fr].last_cmd,
+			DBDMA_NOP | BR_ALWAYS, pb->vid_cbo.bus);
 		eieio();
 		pb->prev_last_fr = pb->last_fr;
 		pb->last_fr = -2;
-		if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
-			IDEBUG("PlanB: became inactive "
+		if(!(ACTIVE & readl(&pb->planb_base->ch1.status))) {
+			IDBG("PlanB: became inactive "
 				"in the mean time... reactivating\n");
 			planb_dbdma_stop(&pb->planb_base->ch1);
-			out_le32 (&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->ch1_cmd));
+			writel(pb->vid_cbo.bus, &pb->planb_base->ch1.cmdptr);
 			planb_dbdma_restart(&pb->planb_base->ch1);
 		}
 	} else {
 
-		DEBUG("PlanB: currently idle, so can do whatever\n");
+		DBG("PlanB: currently idle, so can do whatever\n");
 
 		planb_dbdma_stop(&pb->planb_base->ch2);
 		planb_dbdma_stop(&pb->planb_base->ch1);
-		st_le32 (&pb->planb_base->ch2.cmdptr,
-						virt_to_bus(pb->ch2_cmd));
-		st_le32 (&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->ch1_cmd));
-		out_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+		st_le32(&pb->planb_base->ch2.cmdptr, pb->clip_cbo.bus);
+		st_le32(&pb->planb_base->ch1.cmdptr, pb->vid_cbo.bus);
+		writew(DBDMA_NOP, &pb->vid_cbo.start->command);
 		planb_dbdma_restart(&pb->planb_base->ch2);
 		planb_dbdma_restart(&pb->planb_base->ch1);
 		pb->last_fr = -1;
@@ -477,29 +609,29 @@
 
 static void overlay_stop(struct planb *pb)
 {
-	DEBUG("PlanB: overlay_stop()\n");
+	DBG("PlanB: overlay_stop()\n");
 
 	if(pb->last_fr == -1) {
 
-		DEBUG("PlanB: no grabbing, it seems...\n");
+		DBG("PlanB: no grabbing, it seems...\n");
 
 		planb_dbdma_stop(&pb->planb_base->ch2);
 		planb_dbdma_stop(&pb->planb_base->ch1);
 		pb->last_fr = -999;
 	} else if(pb->last_fr == -2) {
 		unsigned int cmd_dep;
-		tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0);
+		tab_cmd_dbdma(pb->gbuf[pb->prev_last_fr].cap_cmd, DBDMA_STOP, 0);
 		eieio();
-		cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep);
+		cmd_dep = (unsigned int)readl(&pb->overlay_last1->cmd_dep);
 		if(overlay_is_active(pb)) {
 
-			DEBUG("PlanB: overlay is currently active\n");
+			DBG("PlanB: overlay is currently active\n");
 
 			planb_dbdma_stop(&pb->planb_base->ch2);
 			planb_dbdma_stop(&pb->planb_base->ch1);
-			if(cmd_dep != pb->ch1_cmd_phys) {
-				out_le32(&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->overlay_last1));
+			if(cmd_dep != pb->vid_cbo.bus) {
+				writel(virt_to_bus(pb->overlay_last1),
+					&pb->planb_base->ch1.cmdptr);
 				planb_dbdma_restart(&pb->planb_base->ch1);
 			}
 		}
@@ -514,15 +646,15 @@
 	int fr = -1;
 	struct dbdma_cmd last;
 
-	DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend);
+	DBG("PlanB: suspend_overlay: %d\n", pb->suspend);
 
 	if(pb->suspend++)
 		return;
-	if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+	if(ACTIVE & readl(&pb->planb_base->ch1.status)) {
 		if(pb->last_fr == -2) {
 			fr = pb->prev_last_fr;
-			memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last));
-			tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+			memcpy(&last, (void*)pb->gbuf[fr].last_cmd, sizeof(last));
+			tab_cmd_dbdma(pb->gbuf[fr].last_cmd, DBDMA_STOP, 0);
 		}
 		if(overlay_is_active(pb)) {
 			planb_dbdma_stop(&pb->planb_base->ch2);
@@ -542,38 +674,38 @@
 static void resume_overlay(struct planb *pb)
 {
 
-	DEBUG("PlanB: resume_overlay: %d\n", pb->suspend);
+	DBG("PlanB: resume_overlay: %d\n", pb->suspend);
 
 	if(pb->suspend > 1)
 		return;
 	if(pb->suspended.frame != -1) {
-		memcpy((void*)pb->last_cmd[pb->suspended.frame],
-				&pb->suspended.cmd, sizeof(pb->suspended.cmd));
+		memcpy((void*)pb->gbuf[pb->suspended.frame].last_cmd,
+			&pb->suspended.cmd, sizeof(pb->suspended.cmd));
 	}
-	if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+	if(ACTIVE & readl(&pb->planb_base->ch1.status)) {
 		goto finish;
 	}
 	if(pb->suspended.overlay) {
 
-		DEBUG("PlanB: overlay being resumed\n");
+		DBG("PlanB: overlay being resumed\n");
 
-		st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
-		st_le16 (&pb->ch2_cmd->command, DBDMA_NOP);
+		st_le16 (&pb->vid_cbo.start->command, DBDMA_NOP);
+		st_le16 (&pb->clip_cbo.start->command, DBDMA_NOP);
 		/* Set command buffer addresses */
-		st_le32(&pb->planb_base->ch1.cmdptr,
-					virt_to_bus(pb->overlay_last1));
-		out_le32(&pb->planb_base->ch2.cmdptr,
-					virt_to_bus(pb->overlay_last2));
+		writel(virt_to_bus(pb->overlay_last1),
+			&pb->planb_base->ch1.cmdptr);
+		writel(virt_to_bus(pb->overlay_last2),
+			&pb->planb_base->ch2.cmdptr);
 		/* Start the DMA controller */
-		out_le32 (&pb->planb_base->ch2.control,
-				PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
-		out_le32 (&pb->planb_base->ch1.control,
-				PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+		writel(PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE),
+			&pb->planb_base->ch2.control);
+		writel(PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE),
+			&pb->planb_base->ch1.control);
 	} else if(pb->suspended.frame != -1) {
-		out_le32(&pb->planb_base->ch1.cmdptr,
-				virt_to_bus(pb->last_cmd[pb->suspended.frame]));
-		out_le32 (&pb->planb_base->ch1.control,
-				PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+		writel(virt_to_bus(pb->gbuf[pb->suspended.frame].last_cmd),
+			&pb->planb_base->ch1.cmdptr);
+		writel(PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE),
+			&pb->planb_base->ch1.control);
 	}
 
 finish:
@@ -589,7 +721,7 @@
 	int	ww = pb->win.width, hw = pb->win.height;
 	int	x, y, xtmp1, xtmp2;
 
-	DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
+	DBG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
 
 	if(xc < 0) {
 		wc += xc;
@@ -626,47 +758,48 @@
 
 static void fill_cmd_buff(struct planb *pb)
 {
-	int restore = 0;
-	volatile struct dbdma_cmd last;
+	int		restore = 0;
+	dbdma_cmd_t	last;
 
-	DEBUG("PlanB: fill_cmd_buff()\n");
+	DBG("PlanB: fill_cmd_buff()\n");
 
-	if(pb->overlay_last1 != pb->ch1_cmd) {
+	if(pb->overlay_last1 != pb->vid_cbo.start) {
 		restore = 1;
 		last = *(pb->overlay_last1);
 	}
-	memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size
+	memset ((void *) pb->vid_cbo.start, 0, 2 * pb->tab_size
 					* sizeof(struct dbdma_cmd));
 	cmd_buff (pb);
 	if(restore)
 		*(pb->overlay_last1) = last;
 	if(pb->suspended.overlay) {
-		unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep);
-		if(jump_addr != pb->ch1_cmd_phys) {
+		unsigned long jump_addr = readl(&pb->overlay_last1->cmd_dep);
+		if(jump_addr != pb->vid_cbo.bus) {
 			int i;
 
-			DEBUG("PlanB: adjusting ch1's jump address\n");
+			DBG("PlanB: adjusting ch1's jump address\n");
 
 			for(i = 0; i < MAX_GBUFFERS; i++) {
-				if(pb->need_pre_capture[i]) {
-				    if(jump_addr == virt_to_bus(pb->pre_cmd[i]))
+				if(pb->gbuf[i].need_pre_capture) {
+				    if(jump_addr == virt_to_bus(pb->gbuf[i].pre_cmd))
 					goto found;
 				} else {
-				    if(jump_addr == virt_to_bus(pb->cap_cmd[i]))
+				    if(jump_addr ==
+					    virt_to_bus(pb->gbuf[i].cap_cmd))
 					goto found;
 				}
 			}
 
-			DEBUG("PlanB: not found...\n");
+			DBG("       not found!\n");
 
 			goto out;
 found:
-			if(pb->need_pre_capture[i])
-				out_le32(&pb->pre_cmd[i]->phy_addr,
-						virt_to_bus(pb->overlay_last1));
+			if(pb->gbuf[i].need_pre_capture)
+				writel(virt_to_bus(pb->overlay_last1),
+					&pb->gbuf[i].pre_cmd->phy_addr);
 			else
-				out_le32(&pb->cap_cmd[i]->phy_addr,
-						virt_to_bus(pb->overlay_last1));
+				writel(virt_to_bus(pb->overlay_last1),
+					&pb->gbuf[i].cap_cmd->phy_addr);
 		}
 	}
 out:
@@ -679,8 +812,8 @@
 {
 	int		i, bpp, count, nlines, stepsize, interlace;
 	unsigned long	base, jump, addr_com, addr_dep;
-	volatile struct dbdma_cmd *c1 = pb->ch1_cmd;
-	volatile struct dbdma_cmd *c2 = pb->ch2_cmd;
+	dbdma_cmd_ptr	c1 = pb->vid_cbo.start;
+	dbdma_cmd_ptr	c2 = pb->clip_cbo.start;
 
 	interlace = pb->win.interlace;
 	bpp = pb->win.bpp;
@@ -696,33 +829,28 @@
 	addr_dep = virt_to_bus(&c1->cmd_dep);
 	tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
 	jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */
-	if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
-					bpp, 1, pb)) == NULL) {
-		printk(KERN_WARNING "PlanB: encountered serious problems\n");
-		tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0);
-		tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0);
-		return;
-	}
+	c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
+						pb->win.color_fmt, 1, pb);
 	tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16);
 	tab_cmd_store(c1++, addr_dep, jump);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.wait_sel),
 							PLANB_SET(FIELD_SYNC));
 		/* (1) wait for field sync to be set */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 							PLANB_SET(ODD_FIELD));
 		/* wait for field sync to be cleared */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 		/* if not odd field, wait until field sync is set again */
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
 		/* assert ch_sync to ch2 */
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch2.control),
 							PLANB_SET(CH_SYNC));
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 							PLANB_SET(DMA_ABORT));
 
-	base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl
-					+ pb->win.pad) + pb->win.x * bpp);
+	base = (pb->fb.phys + pb->fb.offset + pb->win.y * (pb->win.bpl +
+					pb->win.pad) + pb->win.x * bpp);
 
 	if (interlace) {
 		stepsize = 2;
@@ -744,16 +872,16 @@
 	/* Resync to odd field */
 		/* (2) wait for field sync to be set */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 							PLANB_SET(ODD_FIELD));
 		/* wait for field sync to be cleared */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 		/* if not odd field, wait until field sync is set again */
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
 		/* assert ch_sync to ch2 */
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch2.control),
 							PLANB_SET(CH_SYNC));
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 							PLANB_SET(DMA_ABORT));
 	
 	/* odd field data: */
@@ -765,22 +893,22 @@
 	/* And jump back to the start */
 cmd_tab_data_end:
 	pb->overlay_last1 = c1;	/* keep a pointer to the last command */
-	tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd));
+	tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, pb->vid_cbo.bus);
 
 	/* Clipmask command buffer */
 
 	/* Preamble commands: */
 	tab_cmd_dbdma(c2++, DBDMA_NOP, 0);
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.wait_sel),
 							PLANB_SET(CH_SYNC));
 		/* wait until ch1 asserts ch_sync */
 	tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
 		/* clear ch_sync asserted by ch1 */
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.control),
 							PLANB_CLR(CH_SYNC));
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.wait_sel),
 							PLANB_SET(FIELD_SYNC));
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.br_sel),
 							PLANB_SET(ODD_FIELD));
 
 	/* jump to end of even field if appropriate */
@@ -791,7 +919,7 @@
 	tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump);
 
 	/* even field mask: */
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.br_sel),
 							PLANB_SET(DMA_ABORT));
 	/* this points to pos. B */
 	jump = (interlace) ? virt_to_bus(c2 + nlines + 1):
@@ -806,7 +934,7 @@
 		goto cmd_tab_mask_end;
 
 	/* odd field mask: */
-/* C */	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+/* C */	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch2.br_sel),
 							PLANB_SET(DMA_ABORT));
 	/* this points to pos. B */
 	jump = virt_to_bus(c2 + nlines / 2);
@@ -824,13 +952,13 @@
 		/* corresponds to fsync (1) of ch1 */
 /* B */	tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
 		/* restart ch1, meant to clear any dead bit or something */
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch1.control),
 							PLANB_CLR(RUN));
-	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+	tab_cmd_store(c2++, (unsigned)(&pb->planb_base_bus->ch1.control),
 							PLANB_SET(RUN));
 	pb->overlay_last2 = c2;	/* keep a pointer to the last command */
 		/* start over even field clipmasking */
-	tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd));
+	tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, pb->clip_cbo.bus);
 
 	eieio();
 	return;
@@ -840,40 +968,22 @@
 /* grabdisplay support functions */
 /*********************************/
 
-static int palette2fmt[] = {
-       0,
-       PLANB_GRAY,
-       0,
-       0,
-       0,
-       PLANB_COLOUR32,
-       PLANB_COLOUR15,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-       0,
-};
-
-#define PLANB_PALETTE_MAX 15
-
 static inline int overlay_is_active(struct planb *pb)
 {
 	unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd);
-	unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr);
+	unsigned int caddr = (unsigned)readl(&pb->planb_base->ch1.cmdptr);
 
-	return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys)
-			&& (caddr < (pb->ch1_cmd_phys + size))
-			&& (caddr >= (unsigned)pb->ch1_cmd_phys);
+	return (readl(&pb->overlay_last1->cmd_dep) == pb->vid_cbo.bus)
+			&& (caddr < (pb->vid_cbo.bus + size))
+			&& (caddr >= (unsigned)pb->vid_cbo.bus);
 }
 
 static int vgrab(struct planb *pb, struct video_mmap *mp)
 {
-	unsigned int fr = mp->frame;
-	unsigned int format;
+	unsigned int	fr = mp->frame;
+	unsigned int	fmt = mp->format;
+	unsigned int	bpp = palette2fmt[fmt].bpp;
+	gbuf_ptr	gbuf = &pb->gbuf[fr];
 
 	if(pb->rawbuf==NULL) {
 		int err;
@@ -881,211 +991,225 @@
 			return err;
 	}
 
-	IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
-						mp->width, mp->height, fr);
+	DBG("PlanB: grab %d: %dx%d fmt %d (%u)\n", pb->grabbing, mp->width,
+						mp->height, fmt, fr);
 
-	if(pb->grabbing >= MAX_GBUFFERS)
+	if(pb->grabbing >= MAX_GBUFFERS) {
+		DBG("       no buffer\n");
 		return -ENOBUFS;
-	if(fr > (MAX_GBUFFERS - 1) || fr < 0)
+	}
+	if(fr > (MAX_GBUFFERS - 1) || fr < 0) {
+		DBG("       invalid buffer\n");
 		return -EINVAL;
-	if(mp->height <= 0 || mp->width <= 0)
+	}
+	if(mp->height <= 0 || mp->width <= 0) {
+		DBG("       negative height or width\n");
 		return -EINVAL;
-	if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX)
+	}
+	if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX) {
+		DBG("       format out of range\n");
 		return -EINVAL;
-	if((format = palette2fmt[mp->format]) == 0)
+	}
+	if(bpp == 0) {
+		DBG("       unsupported format %d\n", mp->format);
 		return -EINVAL;
-	if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */
+	}
+	if (mp->height * mp->width * bpp > PLANB_MAX_FBUF) {
+		DBG("       grab bigger than buffer\n");
 		return -EINVAL;
+	}
 
 	planb_lock(pb);
-	if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
-			format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
+	if(mp->width != gbuf->width || mp->height != gbuf->height ||
+			fmt != gbuf->fmt || (gbuf->norm_switch)) {
 		int i;
 #ifndef PLANB_GSCANLINE
-		unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
-								* pb->gfmt[fr];
-		unsigned int nsize = mp->width * mp->height * format;
+		unsigned int osize = gbuf->width * gbuf->height *
+					palette2fmt[gbuf->fmt].bpp;
+		unsigned int nsize = mp->width * mp->height * bpp;
 #endif
 
-		IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n",
-					mp->width, mp->height, mp->format);
+		DBG("PlanB: changed gwidth = %d, gheight = %d, format = %u, "
+			"osize = %d, nsize = %d\n", mp->width, mp->height, fmt,
+								osize, nsize);
 
+/* Do we _really_ need to clear the grab buffers?? */
+#if 0
 #ifndef PLANB_GSCANLINE
-		if(pb->gnorm_switch[fr])
+		if(gbuf->norm_switch)
 			nsize = 0;
 		if (nsize < osize) {
-			for(i = pb->gbuf_idx[fr]; osize > 0; i++) {
+			for(i = gbuf->idx; osize > 0; i++) {
 				memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
 				osize -= PAGE_SIZE;
 			}
 		}
-		for(i = pb->l_fr_addr_idx[fr]; i < pb->l_fr_addr_idx[fr]
-							+ pb->lnum[fr]; i++)
+		for(i = gbuf->l_fr_addr_idx; i <
+				gbuf->l_fr_addr_idx + gbuf->lnum; i++)
 			memset((void *)pb->rawbuf[i], 0, PAGE_SIZE);
 #else
 /* XXX TODO */
 /*
-		if(pb->gnorm_switch[fr])
+		if(gbuf->norm_switch)
 			memset((void *)pb->gbuffer[fr], 0,
-					pb->gbytes_per_line * pb->gheight[fr]);
+					pb->gbytes_per_line * gbuf->height);
 		else {
 			if(mp->
-			for(i = 0; i < pb->gheight[fr]; i++) {
+			for(i = 0; i < gbuf->height; i++) {
 				memset((void *)(pb->gbuffer[fr]
 					+ pb->gbytes_per_line * i
 			}
 		}
 */
 #endif
-		pb->gwidth[fr] = mp->width;
-		pb->gheight[fr] = mp->height;
-		pb->gfmt[fr] = format;
-		pb->last_cmd[fr] = setup_grab_cmd(fr, pb);
-		planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */
-		pb->need_pre_capture[fr] = 1;
-		pb->gnorm_switch[fr] = 0;
+#endif /* if 0 */
+		gbuf->width = mp->width;
+		gbuf->height = mp->height;
+		gbuf->fmt = fmt;
+		gbuf->last_cmd = setup_grab_cmd(fr, pb);
+		planb_pre_capture(fr, pb);
+		gbuf->need_pre_capture = 1;
+		gbuf->norm_switch = 0;
 	} else
-		pb->need_pre_capture[fr] = 0;
-	pb->frame_stat[fr] = GBUFFER_GRABBING;
-	if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+		gbuf->need_pre_capture = 0;
+
+	*gbuf->status = GBUFFER_GRABBING;
+	if(!(ACTIVE & readl(&pb->planb_base->ch1.status))) {
 
-		IDEBUG("PlanB: ch1 inactive, initiating grabbing\n");
+		IDBG("PlanB: ch1 inactive, initiating grabbing\n");
 
 		planb_dbdma_stop(&pb->planb_base->ch1);
-		if(pb->need_pre_capture[fr]) {
+		if(gbuf->need_pre_capture) {
 
-			IDEBUG("PlanB: padding pre-capture sequence\n");
+			DBG("PlanB: padding pre-capture sequence\n");
 
-			out_le32 (&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->pre_cmd[fr]));
+			writel(virt_to_bus(gbuf->pre_cmd),
+				&pb->planb_base->ch1.cmdptr);
 		} else {
-			tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
-			tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+			tab_cmd_dbdma(gbuf->last_cmd, DBDMA_STOP, 0);
+			tab_cmd_dbdma(gbuf->cap_cmd, DBDMA_NOP, 0);
 		/* let's be on the safe side. here is not timing critical. */
-			tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0);
-			out_le32 (&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->cap_cmd[fr]));
+			tab_cmd_dbdma((gbuf->cap_cmd + 1), DBDMA_NOP, 0);
+			writel(virt_to_bus(gbuf->cap_cmd),
+				&pb->planb_base->ch1.cmdptr);
 		}
 		planb_dbdma_restart(&pb->planb_base->ch1);
 		pb->last_fr = fr;
 	} else {
 		int i;
 
-		IDEBUG("PlanB: ch1 active, grabbing being queued\n");
+		DBG("PlanB: ch1 active, grabbing being queued\n");
 
 		if((pb->last_fr == -1) || ((pb->last_fr == -2) &&
 						overlay_is_active(pb))) {
 
-			IDEBUG("PlanB: overlay is active, grabbing defered\n");
+			DBG("PlanB: overlay is active, grabbing defered\n");
 
-			tab_cmd_dbdma(pb->last_cmd[fr],
-					DBDMA_NOP | BR_ALWAYS,
-					virt_to_bus(pb->ch1_cmd));
-			if(pb->need_pre_capture[fr]) {
+			tab_cmd_dbdma(gbuf->last_cmd, DBDMA_NOP | BR_ALWAYS,
+					pb->vid_cbo.bus);
+			if(gbuf->need_pre_capture) {
 
-				IDEBUG("PlanB: padding pre-capture sequence\n");
+				DBG("PlanB: padding pre-capture sequence\n");
 
-				tab_cmd_store(pb->pre_cmd[fr],
+				tab_cmd_store(gbuf->pre_cmd,
 				    virt_to_bus(&pb->overlay_last1->cmd_dep),
-						virt_to_bus(pb->ch1_cmd));
+				    pb->vid_cbo.bus);
 				eieio();
-				out_le32 (&pb->overlay_last1->cmd_dep,
-						virt_to_bus(pb->pre_cmd[fr]));
+				writel(virt_to_bus(gbuf->pre_cmd),
+					&pb->overlay_last1->cmd_dep);
 			} else {
-				tab_cmd_store(pb->cap_cmd[fr],
+				tab_cmd_store(gbuf->cap_cmd,
 				    virt_to_bus(&pb->overlay_last1->cmd_dep),
-						virt_to_bus(pb->ch1_cmd));
-				tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+				    pb->vid_cbo.bus);
+				tab_cmd_dbdma((gbuf->cap_cmd + 1),
 								DBDMA_NOP, 0);
 				eieio();
-				out_le32 (&pb->overlay_last1->cmd_dep,
-						virt_to_bus(pb->cap_cmd[fr]));
+				writel(virt_to_bus(gbuf->cap_cmd),
+					&pb->overlay_last1->cmd_dep);
 			}
 			for(i = 0; overlay_is_active(pb) && i < 999; i++)
-				IDEBUG("PlanB: waiting for overlay done\n");
-			tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
+				DBG("PlanB: waiting for overlay done\n");
+			tab_cmd_dbdma(pb->vid_cbo.start, DBDMA_NOP, 0);
 			pb->prev_last_fr = fr;
 			pb->last_fr = -2;
 		} else if(pb->last_fr == -2) {
 
-			IDEBUG("PlanB: mixed mode detected, grabbing"
+			DBG("PlanB: mixed mode detected, grabbing"
 				" will be done before activating overlay\n");
 
-			tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
-			if(pb->need_pre_capture[fr]) {
+			tab_cmd_dbdma(pb->vid_cbo.start, DBDMA_NOP, 0);
+			if(gbuf->need_pre_capture) {
 
-				IDEBUG("PlanB: padding pre-capture sequence\n");
+				DBG("PlanB: padding pre-capture sequence\n");
 
-				tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+				tab_cmd_dbdma(pb->gbuf[pb->prev_last_fr].last_cmd,
 						DBDMA_NOP | BR_ALWAYS,
-						virt_to_bus(pb->pre_cmd[fr]));
+						virt_to_bus(gbuf->pre_cmd));
 				eieio();
 			} else {
-				tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
-				if(pb->gwidth[pb->prev_last_fr] !=
-								pb->gwidth[fr]
-					|| pb->gheight[pb->prev_last_fr] !=
-								pb->gheight[fr]
-					|| pb->gfmt[pb->prev_last_fr] !=
-								pb->gfmt[fr])
-					tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+				tab_cmd_dbdma(gbuf->cap_cmd, DBDMA_NOP, 0);
+				if(pb->gbuf[pb->prev_last_fr].width !=
+								gbuf->width
+					|| pb->gbuf[pb->prev_last_fr].height !=
+								gbuf->height
+					|| pb->gbuf[pb->prev_last_fr].fmt !=
+								gbuf->fmt)
+					tab_cmd_dbdma((gbuf->cap_cmd + 1),
 								DBDMA_NOP, 0);
 				else
-					tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+					tab_cmd_dbdma((gbuf->cap_cmd + 1),
 					    DBDMA_NOP | BR_ALWAYS,
-					    virt_to_bus(pb->cap_cmd[fr] + 16));
-				tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+					    virt_to_bus(gbuf->cap_cmd + 16));
+				tab_cmd_dbdma(pb->gbuf[pb->prev_last_fr].last_cmd,
 						DBDMA_NOP | BR_ALWAYS,
-						virt_to_bus(pb->cap_cmd[fr]));
+						virt_to_bus(gbuf->cap_cmd));
 				eieio();
 			}
-			tab_cmd_dbdma(pb->last_cmd[fr],
-					DBDMA_NOP | BR_ALWAYS,
-					virt_to_bus(pb->ch1_cmd));
+			tab_cmd_dbdma(gbuf->last_cmd, DBDMA_NOP | BR_ALWAYS,
+				pb->vid_cbo.bus);
 			eieio();
 			pb->prev_last_fr = fr;
 			pb->last_fr = -2;
 		} else {
+			gbuf_ptr	lastgbuf = &pb->gbuf[pb->last_fr];
 
-			IDEBUG("PlanB: active grabbing session detected\n");
+			DBG("PlanB: active grabbing session detected\n");
 
-			if(pb->need_pre_capture[fr]) {
+			if(gbuf->need_pre_capture) {
 
-				IDEBUG("PlanB: padding pre-capture sequence\n");
+				DBG("PlanB: padding pre-capture sequence\n");
 
-				tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+				tab_cmd_dbdma(lastgbuf->last_cmd,
 						DBDMA_NOP | BR_ALWAYS,
-						virt_to_bus(pb->pre_cmd[fr]));
+						virt_to_bus(gbuf->pre_cmd));
 				eieio();
 			} else {
-				tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
-				tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
-				if(pb->gwidth[pb->last_fr] != pb->gwidth[fr]
-					|| pb->gheight[pb->last_fr] !=
-								pb->gheight[fr]
-					|| pb->gfmt[pb->last_fr] !=
-								pb->gfmt[fr])
-					tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+				tab_cmd_dbdma(gbuf->last_cmd, DBDMA_STOP, 0);
+				tab_cmd_dbdma(gbuf->cap_cmd, DBDMA_NOP, 0);
+				if(lastgbuf->width != gbuf->width
+				    || lastgbuf->height != gbuf->height
+				    || lastgbuf->fmt != gbuf->fmt)
+					tab_cmd_dbdma((gbuf->cap_cmd + 1),
 								DBDMA_NOP, 0);
 				else
-					tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+					tab_cmd_dbdma((gbuf->cap_cmd + 1),
 					    DBDMA_NOP | BR_ALWAYS,
-					    virt_to_bus(pb->cap_cmd[fr] + 16));
-				tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+					    virt_to_bus(gbuf->cap_cmd + 16));
+				tab_cmd_dbdma(lastgbuf->last_cmd,
 						DBDMA_NOP | BR_ALWAYS,
-						virt_to_bus(pb->cap_cmd[fr]));
+						virt_to_bus(gbuf->cap_cmd));
 				eieio();
 			}
 			pb->last_fr = fr;
 		}
-		if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+		if(!(ACTIVE & readl(&pb->planb_base->ch1.status))) {
 
-			IDEBUG("PlanB: became inactive in the mean time..."
+			DBG("PlanB: became inactive in the mean time... "
 				"reactivating\n");
 
 			planb_dbdma_stop(&pb->planb_base->ch1);
-			out_le32 (&pb->planb_base->ch1.cmdptr,
-						virt_to_bus(pb->cap_cmd[fr]));
+			writel(virt_to_bus(gbuf->cap_cmd),
+				&pb->planb_base->ch1.cmdptr);
 			planb_dbdma_restart(&pb->planb_base->ch1);
 		}
 	}
@@ -1095,49 +1219,58 @@
 	return 0;
 }
 
-static void planb_pre_capture(int fr, int bpp, struct planb *pb)
+static void planb_pre_capture(int fr, struct planb *pb)
 {
-	volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr];
-	int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
+	gbuf_ptr	gbuf = &pb->gbuf[fr];
+	dbdma_cmd_ptr	c1 = gbuf->pre_cmd;
+	int		height = gbuf->height;
+	int		interlace = (height > pb->maxlines/2)? 1: 0;
 
 	tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
-	if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
-						bpp, 0, pb)) == NULL) {
-		printk(KERN_WARNING "PlanB: encountered some problems\n");
-		tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0);
-		return;
-	}
+	c1 = cmd_geo_setup(c1, gbuf->width, height, interlace, gbuf->fmt,
+									0, pb);
 	/* Sync to even field */
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
-		PLANB_SET(FIELD_SYNC));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.wait_sel),
+							PLANB_SET(FIELD_SYNC));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(ODD_FIELD));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(ODD_FIELD));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
 	tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(DMA_ABORT));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(DMA_ABORT));
 	/* For non-interlaced, we use even fields only */
-	if (pb->gheight[fr] <= pb->maxlines/2)
+	if (interlace == 0)
 		goto cmd_tab_data_end;
 	/* Sync to odd field */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(ODD_FIELD));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(ODD_FIELD));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(DMA_ABORT));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(DMA_ABORT));
 cmd_tab_data_end:
-	tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr]));
+	tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(gbuf->cap_cmd));
 
 	eieio();
 }
 
-static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb)
+/* This needs some explanation.
+ * What we do here is write the DBDMA commands to fill the grab buffer.
+ * Since the grab buffer is made up of physically non-contiguous chunks,
+ * we need to make sure to not make the DMA engine write across a chunk
+ * boundary: the DMA engine needs a physically contiguous memory chunk for
+ * a single scan line.
+ * So all those scan lines that cross a chunk boundary are written do spare
+ * scratch buffers, and we keep track of this fact.
+ * Later, in the interrupt routine, we copy those scan lines (in two pieces)
+ * back to where they belong in the right sequence in the grab buffer.
+ */
+static dbdma_cmd_ptr setup_grab_cmd(int fr, struct planb *pb)
 {
-	int		i, bpp, count, nlines, stepsize, interlace;
+	int		i, count, nlines, stepsize, interlace;
 #ifdef PLANB_GSCANLINE
 	int		scanline;
 #else
@@ -1146,19 +1279,20 @@
 #endif
 	unsigned long	jump;
 	int		pagei;
-	volatile struct dbdma_cmd *c1;
-	volatile struct dbdma_cmd *jump_addr;
-
-	c1 = pb->cap_cmd[fr];
-	interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
-	bpp = pb->gfmt[fr];	/* gfmt = bpp */
-	count = bpp * pb->gwidth[fr];
-	nlines = pb->gheight[fr];
+	dbdma_cmd_ptr	c1;
+	dbdma_cmd_ptr	jump_addr;
+	gbuf_ptr	gbuf = &pb->gbuf[fr];
+	int		fmt = gbuf->fmt;
+
+	c1 = gbuf->cap_cmd;
+	nlines = gbuf->height;
+	interlace = (nlines > pb->maxlines/2) ? 1 : 0;
+	count = palette2fmt[fmt].bpp * gbuf->width;
 #ifdef PLANB_GSCANLINE
 	scanline = pb->gbytes_per_line;
 #else
-	pb->lsize[fr] = count;
-	pb->lnum[fr] = 0;
+	gbuf->lsize = count;
+	gbuf->lnum = 0;
 #endif
 
 	/* Do video in: */
@@ -1166,22 +1300,17 @@
 	/* Preamble commands: */
 	tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++;
-	if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
-						bpp, 0, pb)) == NULL) {
-		printk(KERN_WARNING "PlanB: encountered serious problems\n");
-		tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0);
-		return (pb->cap_cmd[fr] + 2);
-	}
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
-		PLANB_SET(FIELD_SYNC));
+	c1 = cmd_geo_setup(c1, gbuf->width, nlines, interlace, fmt, 0, pb);
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.wait_sel),
+							PLANB_SET(FIELD_SYNC));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(ODD_FIELD));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(ODD_FIELD));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
 	tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
-		PLANB_SET(DMA_ABORT));
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
+							PLANB_SET(DMA_ABORT));
 
 	if (interlace) {
 		stepsize = 2;
@@ -1194,12 +1323,12 @@
 
 	/* even field data: */
 
-	pagei = pb->gbuf_idx[fr];
+	pagei = gbuf->idx;
 #ifdef PLANB_GSCANLINE
 	for (i = 0; i < nlines; i += stepsize) {
-		tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
-					virt_to_bus(pb->rawbuf[pagei
-					+ i * scanline / PAGE_SIZE]), jump);
+		tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+		    virt_to_bus(pb->rawbuf[pagei + i * scanline / PAGE_SIZE]),
+									jump);
 	}
 #else
 	i = 0;
@@ -1219,18 +1348,25 @@
 		    leftover1 = 0;
 		else {
 		    if(lov0 >= count) {
+			/* can happen only when interlacing; then other field
+			 * uses up leftover space (lov0 - count). */
 			tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
 				+ count * nlpp * stepsize + leftover1, jump);
 		    } else {
-			pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+			/* start of free space at end of page: */
+			pb->l_to_addr[fr][gbuf->lnum] = pb->rawbuf[pagei]
 					+ count * nlpp * stepsize + leftover1;
-			pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
-			pb->l_to_next_size[fr][pb->lnum[fr]] = count - lov0;
+			/* index where continuation is: */
+			pb->l_to_next_idx[fr][gbuf->lnum] = pagei + 1;
+			/* How much is left to do in next page: */
+			pb->l_to_next_size[fr][gbuf->lnum] = count - lov0;
 			tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
-				virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
-						+ pb->lnum[fr]]), jump);
-			if(++pb->lnum[fr] > MAX_LNUM)
-				pb->lnum[fr]--;
+				virt_to_bus(pb->rawbuf[gbuf->l_fr_addr_idx
+						+ gbuf->lnum]), jump);
+			if(++gbuf->lnum > MAX_LNUM) {
+				/* FIXME: error condition! */
+				gbuf->lnum--;
+		    	}
 		    }
 		    leftover1 = count * stepsize - lov0;
 		    i += stepsize;
@@ -1248,11 +1384,11 @@
 
 	/* Sync to odd field */
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 		PLANB_SET(ODD_FIELD));
 	tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
 	tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->ch1.br_sel),
 		PLANB_SET(DMA_ABORT));
 	
 	/* odd field data: */
@@ -1260,14 +1396,14 @@
 	jump = virt_to_bus(jump_addr);
 #ifdef PLANB_GSCANLINE
 	for (i = 1; i < nlines; i += stepsize) {
-		tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+		tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
 					virt_to_bus(pb->rawbuf[pagei
 					+ i * scanline / PAGE_SIZE]), jump);
 	}
 #else
 	i = 1;
 	leftover1 = 0;
-	pagei = pb->gbuf_idx[fr];
+	pagei = gbuf->idx;
 	if(nlines <= 1)
 	    goto skip;
 	do {
@@ -1290,16 +1426,18 @@
 		    leftover1 = 0;
 		else {
 		    if(lov0 > count) {
-			pb->l_to_addr[fr][pb->lnum[fr]] = pb->rawbuf[pagei]
+			pb->l_to_addr[fr][gbuf->lnum] = pb->rawbuf[pagei]
 				+ count * (nlpp * stepsize + 1) + leftover1;
-			pb->l_to_next_idx[fr][pb->lnum[fr]] = pagei + 1;
-			pb->l_to_next_size[fr][pb->lnum[fr]] = count * stepsize
+			pb->l_to_next_idx[fr][gbuf->lnum] = pagei + 1;
+			pb->l_to_next_size[fr][gbuf->lnum] = count * stepsize
 									- lov0;
 			tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
-				virt_to_bus(pb->rawbuf[pb->l_fr_addr_idx[fr]
-							+ pb->lnum[fr]]), jump);
-			if(++pb->lnum[fr] > MAX_LNUM)
-				pb->lnum[fr]--;
+				virt_to_bus(pb->rawbuf[gbuf->l_fr_addr_idx
+							+ gbuf->lnum]), jump);
+			if(++gbuf->lnum > MAX_LNUM) {
+				/* FIXME: error condition! */
+				gbuf->lnum--;
+			}
 			i += stepsize;
 		    }
 		    leftover1 = count * stepsize - lov0;
@@ -1313,7 +1451,7 @@
 #endif /* PLANB_GSCANLINE */
 
 cmd_tab_data_end:
-	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
+	tab_cmd_store(c1++, (unsigned)(&pb->planb_base_bus->intr_stat),
 			(fr << 9) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
 	/* stop it */
 	tab_cmd_dbdma(c1, DBDMA_STOP, 0);
@@ -1327,50 +1465,54 @@
 	unsigned int stat, astat;
 	struct planb *pb = (struct planb *)dev_id;
 
-	IDEBUG("PlanB: planb_irq()\n");
+	IDBG("PlanB: planb_irq()\n");
 
 	/* get/clear interrupt status bits */
 	eieio();
-	stat = in_le32(&pb->planb_base->intr_stat);
+	stat = readl(&pb->planb_base->intr_stat);
 	astat = stat & pb->intr_mask;
-	out_le32(&pb->planb_base->intr_stat, PLANB_FRM_IRQ
-					& ~astat & stat & ~PLANB_GEN_IRQ);
-	IDEBUG("PlanB: stat = %X, astat = %X\n", stat, astat);
+	writel(PLANB_FRM_IRQ & ~astat & stat & ~PLANB_GEN_IRQ,
+		&pb->planb_base->intr_stat);
+	IDBG("PlanB: stat = %X, astat = %X\n", stat, astat);
 
 	if(astat & PLANB_FRM_IRQ) {
-		unsigned int fr = stat >> 9;
+		unsigned int	fr = stat >> 9;
+		gbuf_ptr	gbuf = &pb->gbuf[fr];
 #ifndef PLANB_GSCANLINE
-		int i;
+		int		i;
 #endif
-		IDEBUG("PlanB: PLANB_FRM_IRQ\n");
+		IDBG("PlanB: PLANB_FRM_IRQ\n");
 
 		pb->gcount++;
 
-		IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n",
+		IDBG("PlanB: grab %d: fr = %d, gcount = %d\n",
 				pb->grabbing, fr, pb->gcount);
 #ifndef PLANB_GSCANLINE
-		IDEBUG("PlanB: %d * %d bytes are being copied over\n",
-				pb->lnum[fr], pb->lsize[fr]);
-		for(i = 0; i < pb->lnum[fr]; i++) {
-			int first = pb->lsize[fr] - pb->l_to_next_size[fr][i];
+		/* Now that the buffer is full, copy those lines that fell
+		 * on a page boundary from the spare buffers back to where
+		 * they belong. */
+		IDBG("PlanB: %d * %d bytes are being copied over\n",
+				gbuf->lnum, gbuf->lsize);
+		for(i = 0; i < gbuf->lnum; i++) {
+			int first = gbuf->lsize - pb->l_to_next_size[fr][i];
 
 			memcpy(pb->l_to_addr[fr][i],
-				pb->rawbuf[pb->l_fr_addr_idx[fr] + i],
+				pb->rawbuf[gbuf->l_fr_addr_idx + i],
 				first);
 			memcpy(pb->rawbuf[pb->l_to_next_idx[fr][i]],
-				pb->rawbuf[pb->l_fr_addr_idx[fr] + i] + first,
+				pb->rawbuf[gbuf->l_fr_addr_idx + i] + first,
 						pb->l_to_next_size[fr][i]);
 		}
 #endif
-		pb->frame_stat[fr] = GBUFFER_DONE;
+		*gbuf->status = GBUFFER_DONE;
 		pb->grabbing--;
 		wake_up_interruptible(&pb->capq);
 		return;
 	}
 	/* incorrect interrupts? */
 	pb->intr_mask = PLANB_CLR_IRQ;
-	out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
-	printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts"
+	writel(PLANB_CLR_IRQ, &pb->planb_base->intr_stat);
+	printk(KERN_ERR "PlanB: IRQ lockup, cleared interrupts"
 							" unconditionally\n");
 }
 
@@ -1380,16 +1522,22 @@
 
 static int planb_open(struct video_device *dev, int mode)
 {
-	struct planb *pb = (struct planb *)dev;
+	struct planb	*pb = (struct planb *)dev->priv;
+	int		err;
 
-	if (pb->user == 0) {
-		int err;
+	/* first open on driver? */
+	if(pb->vid_user + pb->vbi_user == 0) {
 		if((err = planb_prepare_open(pb)) != 0)
 			return err;
 	}
-	pb->user++;
+	/* first open on video dev? */
+	if(pb->vid_user == 0) {
+		if((err = planb_prepare_video(pb)) != 0)
+			return err;
+	}
+	pb->vid_user++;
 
-	DEBUG("PlanB: device opened\n");
+	DBG("PlanB: device opened\n");
 
 	MOD_INC_USE_COUNT;
 	return 0;   
@@ -1397,44 +1545,46 @@
 
 static void planb_close(struct video_device *dev)
 {
-	struct planb *pb = (struct planb *)dev;
+	struct planb *pb = (struct planb *)dev->priv;
 
-	if(pb->user < 1) /* ??? */
-		return;
 	planb_lock(pb);
-	if (pb->user == 1) {
-		if (pb->overlay) {
+	/* last close? then stop everything... */
+	if(--pb->vid_user == 0) {
+		if(pb->overlay) {
 			planb_dbdma_stop(&pb->planb_base->ch2);
 			planb_dbdma_stop(&pb->planb_base->ch1);
 			pb->overlay = 0;
 		}
-		planb_prepare_close(pb);
+		planb_close_video(pb);
 	}
-	pb->user--;
+	/* last open on PlanB hardware? */
+	if(pb->vid_user + pb->vbi_user == 0)
+		planb_prepare_close(pb);
 	planb_unlock(pb);
 
-	DEBUG("PlanB: device closed\n");
+	DBG("PlanB: device closed\n");
 
-	MOD_DEC_USE_COUNT;  
+	MOD_DEC_USE_COUNT;
+	return;
 }
 
 static long planb_read(struct video_device *v, char *buf, unsigned long count,
 				int nonblock)
 {
-	DEBUG("planb: read request\n");
+	DBG("planb: read request\n");
 	return -EINVAL;
 }
 
 static long planb_write(struct video_device *v, const char *buf,
 				unsigned long count, int nonblock)
 {
-	DEBUG("planb: write request\n");
+	DBG("planb: write request\n");
 	return -EINVAL;
 }
 
 static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
 {
-	struct planb *pb=(struct planb *)dev;
+	struct planb *pb=(struct planb *)dev->priv;
   	
 	switch (cmd)
 	{	
@@ -1442,7 +1592,7 @@
 		{
 			struct video_capability b;
 
-			DEBUG("PlanB: IOCTL VIDIOCGCAP\n");
+			DBG("PlanB: IOCTL VIDIOCGCAP\n");
 
 			strcpy (b.name, pb->video_dev.name);
 			b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
@@ -1461,50 +1611,48 @@
 		case VIDIOCSFBUF:
 		{
                         struct video_buffer v;
-			unsigned short bpp;
 			unsigned int fmt;
 
-			DEBUG("PlanB: IOCTL VIDIOCSFBUF\n");
+			DBG("PlanB: IOCTL VIDIOCSFBUF\n");
 
-                        if (!capable(CAP_SYS_ADMIN)
-			|| !capable(CAP_SYS_RAWIO))
+                        if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
                                 return -EPERM;
-                        if (copy_from_user(&v, arg,sizeof(v)))
+                        if (copy_from_user(&v, arg, sizeof(v)))
                                 return -EFAULT;
 			planb_lock(pb);
 			switch(v.depth) {
-			case 8:
-				bpp = 1;
-				fmt = PLANB_GRAY;
+			    /* xawtv only asks for 8 bit in static grey, but
+			     * there is no way to know what it really means.. */
+			    case 8:
+				fmt = VIDEO_PALETTE_GREY;
 				break;
-			case 15:
-			case 16:
-				bpp = 2;
-				fmt = PLANB_COLOUR15;
+			    case 15:
+				fmt = VIDEO_PALETTE_RGB555;
 				break;
-			case 24:
-			case 32:
-				bpp = 4;
-				fmt = PLANB_COLOUR32;
+			    case 32:
+				fmt = VIDEO_PALETTE_RGB32;
 				break;
-			default:
+			    /* We don't deliver these two... */
+			    case 16:
+			    case 24:
+			    default:
 				planb_unlock(pb);
                                 return -EINVAL;
 			}
-			if (bpp * v.width > v.bytesperline) {
+			if (palette2fmt[fmt].bpp * v.width > v.bytesperline) {
 				planb_unlock(pb);
 				return -EINVAL;
 			}
-			pb->win.bpp = bpp;
+			pb->win.bpp = palette2fmt[fmt].bpp;
 			pb->win.color_fmt = fmt;
-			pb->frame_buffer_phys = (unsigned long) v.base;
+			pb->fb.phys = (unsigned long) v.base;
 			pb->win.sheight = v.height;
 			pb->win.swidth = v.width;
 			pb->picture.depth = pb->win.depth = v.depth;
 			pb->win.bpl = pb->win.bpp * pb->win.swidth;
 			pb->win.pad = v.bytesperline - pb->win.bpl;
 
-                        DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d,"
+                        DBG("PlanB: Display at %p is %d by %d, bytedepth %d,"
 				" bpl %d (+ %d)\n", v.base, v.width,v.height,
 				pb->win.bpp, pb->win.bpl, pb->win.pad);
 
@@ -1521,9 +1669,9 @@
 		{
                         struct video_buffer v;
 
-			DEBUG("PlanB: IOCTL VIDIOCGFBUF\n");
+			DBG("PlanB: IOCTL VIDIOCGFBUF\n");
 
-			v.base = (void *)pb->frame_buffer_phys;
+			v.base = (void *)pb->fb.phys;
 			v.height = pb->win.sheight;
 			v.width = pb->win.swidth;
 			v.depth = pb->win.depth;
@@ -1539,7 +1687,7 @@
                         if(copy_from_user(&i, arg, sizeof(i)))
                                 return -EFAULT;
 			if(i==0) {
-				DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
+				DBG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
 
 				if (!(pb->overlay))
 					return 0;
@@ -1548,9 +1696,9 @@
 				overlay_stop(pb);
 				planb_unlock(pb);
 			} else {
-				DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
+				DBG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
 
-				if (pb->frame_buffer_phys == 0 ||
+				if (pb->fb.phys == 0 ||
 					  pb->win.width == 0 ||
 					  pb->win.height == 0)
 					return -EINVAL;
@@ -1569,7 +1717,7 @@
 		{
 			struct video_channel v;
 
-			DEBUG("PlanB: IOCTL VIDIOCGCHAN\n");
+			DBG("PlanB: IOCTL VIDIOCGCHAN\n");
 
 			if(copy_from_user(&v, arg,sizeof(v)))
 				return -EFAULT;
@@ -1598,7 +1746,7 @@
 		{
 			struct video_channel v;
 
-			DEBUG("PlanB: IOCTL VIDIOCSCHAN\n");
+			DBG("PlanB: IOCTL VIDIOCSCHAN\n");
 
 			if(copy_from_user(&v, arg, sizeof(v)))
 				return -EFAULT;
@@ -1616,6 +1764,7 @@
 					maxlines = PLANB_NTSC_MAXLINES;
 					break;
 				default:
+					DBG("       invalid norm %d.\n", v.norm);
 					return -EINVAL;
 					break;
 				}
@@ -1628,7 +1777,7 @@
 				/* Stop overlay if running */
 				suspend_overlay(pb);
 				for(i = 0; i < MAX_GBUFFERS; i++)
-					pb->gnorm_switch[i] = 1;
+					pb->gbuf[i].norm_switch = 1;
 				/* I know it's an overkill, but.... */
 				fill_cmd_buff(pb);
 				/* ok, now init it accordingly */
@@ -1651,6 +1800,7 @@
 					  ~7) | 4), pb);
 				break;
 			default:
+				DBG("       invalid channel %d.\n", v.channel);
 				return -EINVAL;
 				break;
 			}
@@ -1661,22 +1811,9 @@
 		{
 			struct video_picture vp = pb->picture;
 
-			DEBUG("PlanB: IOCTL VIDIOCGPICT\n");
-
-			switch(pb->win.color_fmt) {
-			case PLANB_GRAY:
-				vp.palette = VIDEO_PALETTE_GREY;
-			case PLANB_COLOUR15:
-				vp.palette = VIDEO_PALETTE_RGB555;
-				break;
-			case PLANB_COLOUR32:
-				vp.palette = VIDEO_PALETTE_RGB32;
-				break;
-			default:
-				vp.palette = 0;
-				break;
-			}
+			DBG("PlanB: IOCTL VIDIOCGPICT\n");
 
+			vp.palette = pb->win.color_fmt;
 			if(copy_to_user(arg,&vp,sizeof(vp)))
 				return -EFAULT;
 			return 0;
@@ -1685,12 +1822,13 @@
 		{
 			struct video_picture vp;
 
-			DEBUG("PlanB: IOCTL VIDIOCSPICT\n");
+			DBG("PlanB: IOCTL VIDIOCSPICT\n");
 
 			if(copy_from_user(&vp,arg,sizeof(vp)))
 				return -EFAULT;
 			pb->picture = vp;
 			/* Should we do sanity checks here? */
+			planb_lock(pb);
 			saa_set (SAA7196_BRIG, (unsigned char)
 			    ((pb->picture.brightness) >> 8), pb);
 			saa_set (SAA7196_HUEC, (unsigned char)
@@ -1699,6 +1837,7 @@
 			    ((pb->picture.colour) >> 9), pb);
 			saa_set (SAA7196_CONT, (unsigned char)
 			    ((pb->picture.contrast) >> 9), pb);
+			planb_unlock(pb);
 
 			return 0;
 		}
@@ -1708,7 +1847,7 @@
 			struct video_clip	clip;
 			int 			i;
 			
-			DEBUG("PlanB: IOCTL VIDIOCSWIN\n");
+			DBG("PlanB: IOCTL VIDIOCSWIN\n");
 
 			if(copy_from_user(&vw,arg,sizeof(vw)))
 				return -EFAULT;
@@ -1728,6 +1867,9 @@
 				pb->win.height = vw.height;
 				fill_cmd_buff(pb);
 			}
+                        DBG("PlanB: Window at (%d,%d) size %dx%d\n", vw.x, vw.y, vw.width,
+				vw.height);
+
 			/* Reset clip mask */
 			memset ((void *) pb->mask, 0xff, (pb->maxlines
 					* ((PLANB_MAXPIXELS + 7) & ~7)) / 8);
@@ -1747,7 +1889,7 @@
 		{
 			struct video_window vw;
 
-			DEBUG("PlanB: IOCTL VIDIOCGWIN\n");
+			DBG("PlanB: IOCTL VIDIOCGWIN\n");
 
 			vw.x=pb->win.x;
 			vw.y=pb->win.y;
@@ -1762,30 +1904,32 @@
 			return 0;
 		}
 	        case VIDIOCSYNC: {
-			int i;
+			int		i;
+			gbuf_ptr	gbuf;
 
-			IDEBUG("PlanB: IOCTL VIDIOCSYNC\n");
+			DBG("PlanB: IOCTL VIDIOCSYNC\n");
 
 			if(copy_from_user((void *)&i,arg,sizeof(int)))
 				return -EFAULT;
 
-			IDEBUG("PlanB: sync to frame %d\n", i);
+			DBG("PlanB: sync to frame %d\n", i);
 
                         if(i > (MAX_GBUFFERS - 1) || i < 0)
                                 return -EINVAL;
+			gbuf = &pb->gbuf[i];
 chk_grab:
-                        switch (pb->frame_stat[i]) {
+                        switch (*gbuf->status) {
                         case GBUFFER_UNUSED:
                                 return -EINVAL;
 			case GBUFFER_GRABBING:
-				IDEBUG("PlanB: waiting for grab"
+				DBG("PlanB: waiting for grab"
 							" done (%d)\n", i);
  			        interruptible_sleep_on(&pb->capq);
 				if(signal_pending(current))
 					return -EINTR;
 				goto chk_grab;
                         case GBUFFER_DONE:
-                                pb->frame_stat[i] = GBUFFER_UNUSED;
+                                *gbuf->status = GBUFFER_UNUSED;
                                 break;
                         }
                         return 0;
@@ -1794,17 +1938,19 @@
 	        case VIDIOCMCAPTURE:
 		{
                         struct video_mmap vm;
-			volatile unsigned int status;
+			int		  fr;
 
-			IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n");
+			DBG("PlanB: IOCTL VIDIOCMCAPTURE\n");
 
 			if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm)))
 				return -EFAULT;
-                        status = pb->frame_stat[vm.frame];
-                        if (status != GBUFFER_UNUSED)
-                                return -EBUSY;
+			fr = vm.frame;
+                        if(fr > (MAX_GBUFFERS - 1) || fr < 0)
+                                return -EINVAL;
+			if (*pb->gbuf[fr].status != GBUFFER_UNUSED)
+				return -EBUSY;
 
-		        return vgrab(pb, &vm);
+			return vgrab(pb, &vm);
 		}
 		
 		case VIDIOCGMBUF:
@@ -1812,7 +1958,7 @@
 			int i;
 			struct video_mbuf vm;
 
-			DEBUG("PlanB: IOCTL VIDIOCGMBUF\n");
+			DBG("PlanB: IOCTL VIDIOCGMBUF\n");
 
 			memset(&vm, 0 , sizeof(vm));
 			vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS;
@@ -1824,11 +1970,27 @@
 			return 0;
 		}
 		
+		case VIDIOCGUNIT:
+		{
+			struct video_unit vu;
+
+			DBG("PlanB: IOCTL VIDIOCGUNIT\n");
+
+			vu.video=pb->video_dev.minor;
+			vu.vbi=pb->vbi_dev.minor;
+			vu.radio=VIDEO_NO_UNIT;
+			vu.audio=VIDEO_NO_UNIT;
+			vu.teletext=VIDEO_NO_UNIT;
+			if(copy_to_user((void *)arg, (void *)&vu, sizeof(vu)))
+				return -EFAULT;
+			return 0;
+		}
+
 		case PLANBIOCGSAAREGS:
 		{
 			struct planb_saa_regs preg;
 
-			DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
+			DBG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
 
 			if(copy_from_user(&preg, arg, sizeof(preg)))
 				return -EFAULT;
@@ -1845,7 +2007,7 @@
 		{
 			struct planb_saa_regs preg;
 
-			DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
+			DBG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
 
 			if(copy_from_user(&preg, arg, sizeof(preg)))
 				return -EFAULT;
@@ -1859,10 +2021,14 @@
 		{
 			struct planb_stat_regs pstat;
 
-			DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n");
+			DBG("PlanB: IOCTL PLANBIOCGSTAT\n");
 
-			pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status);
-			pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status);
+			pstat.ch1_stat = readl(&pb->planb_base->ch1.status);
+			pstat.ch2_stat = readl(&pb->planb_base->ch2.status);
+			pstat.ch1_cmdbase = (unsigned long)pb->vid_cbo.start;
+			pstat.ch2_cmdbase = (unsigned long)pb->clip_cbo.start;
+			pstat.ch1_cmdptr = readl(&pb->planb_base->ch1.cmdptr);
+			pstat.ch2_cmdptr = readl(&pb->planb_base->ch2.cmdptr);
 			pstat.saa_stat0 = saa_status(0, pb);
 			pstat.saa_stat1 = saa_status(1, pb);
 
@@ -1871,11 +2037,11 @@
 				return -EFAULT;
 			return 0;
 		}
-		
+
 		case PLANBIOCSMODE: {
 			int v;
 
-			DEBUG("PlanB: IOCTL PLANBIOCSMODE\n");
+			DBG("PlanB: IOCTL PLANBIOCSMODE\n");
 
 			if(copy_from_user(&v, arg, sizeof(v)))
 				return -EFAULT;
@@ -1902,7 +2068,7 @@
 		case PLANBIOCGMODE: {
 			int v=pb->win.mode;
 
-			DEBUG("PlanB: IOCTL PLANBIOCGMODE\n");
+			DBG("PlanB: IOCTL PLANBIOCGMODE\n");
 
 			if(copy_to_user(arg,&v,sizeof(v)))
 				return -EFAULT;
@@ -1912,25 +2078,28 @@
 		case PLANBG_GRAB_BPL: {
 			int v=pb->gbytes_per_line;
 
-			DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
+			DBG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
 
 			if(copy_to_user(arg,&v,sizeof(v)))
 				return -EFAULT;
 			return 0;
 		}
 #endif /* PLANB_GSCANLINE */
+
+/* These serve only for debugging... */
+#ifdef DEBUG
 		case PLANB_INTR_DEBUG: {
 			int i;
 
-			DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
+			DBG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
 
 			if(copy_from_user(&i, arg, sizeof(i)))
 				return -EFAULT;
 
 			/* avoid hang ups all together */
 			for (i = 0; i < MAX_GBUFFERS; i++) {
-				if(pb->frame_stat[i] == GBUFFER_GRABBING) {
-					pb->frame_stat[i] = GBUFFER_DONE;
+				if(*pb->gbuf[i].status == GBUFFER_GRABBING) {
+					*pb->gbuf[i].status = GBUFFER_DONE;
 				}
 			}
 			if(pb->grabbing)
@@ -1942,7 +2111,7 @@
 			int i;
 			struct planb_any_regs any;
 
-			DEBUG("PlanB: IOCTL PLANB_INV_REGS\n");
+			DBG("PlanB: IOCTL PLANB_INV_REGS\n");
 
 			if(copy_from_user(&any, arg, sizeof(any)))
 				return -EFAULT;
@@ -1952,42 +2121,70 @@
 				return -EINVAL;
 			for (i = 0; i < any.bytes; i++) {
 				any.data[i] =
-					in_8((unsigned char *)pb->planb_base
+					readb((unsigned char *)pb->planb_base
 							+ any.offset + i);
 			}
 			if(copy_to_user(arg,&any,sizeof(any)))
 				return -EFAULT;
 			return 0;
 		}
+		case PLANBIOCGDBDMABUF:
+		{
+			struct planb_buf_regs buf;
+			dbdma_cmd_ptr dc;
+			int i;
+
+			DBG("PlanB: IOCTL PLANBIOCGDBDMABUF\n");
+
+			if(copy_from_user(&buf, arg, sizeof(buf)))
+				return -EFAULT;
+			buf.end &= ~0xf;
+			if( (buf.start < 0) || (buf.end < 0x10) ||
+			    (buf.end < buf.start+0x10) ||
+			    (buf.end > 2*pb->tab_size) )
+				return -EINVAL;
+
+			printk ("PlanB DBDMA command buffer:\n");
+			for (i=(buf.start>>4); i<=(buf.end>>4); i++) {
+				printk(" 0x%04x:", i<<4);
+				dc = pb->vid_cbo.start + i;
+				printk (" %04x %04x %08x %08x %04x %04x\n",
+				  dc->req_count, dc->command, dc->phy_addr,
+				  dc->cmd_dep, dc->res_count, dc->xfer_status);
+			}
+			return 0;
+		}
+#endif /* DEBUG */
+
 		default:
 		{
-			DEBUG("PlanB: Unimplemented IOCTL\n");
+			DBG("PlanB: Unimplemented IOCTL: %d (0x%x)\n", cmd, cmd);
 			return -ENOIOCTLCMD;
 		}
 	/* Some IOCTLs are currently unsupported on PlanB */
 		case VIDIOCGTUNER: {
-		DEBUG("PlanB: IOCTL VIDIOCGTUNER\n");
+		DBG("PlanB: IOCTL VIDIOCGTUNER\n");
 			goto unimplemented; }
 		case VIDIOCSTUNER: {
-		DEBUG("PlanB: IOCTL VIDIOCSTUNER\n");
+		DBG("PlanB: IOCTL VIDIOCSTUNER\n");
 			goto unimplemented; }
 		case VIDIOCSFREQ: {
-		DEBUG("PlanB: IOCTL VIDIOCSFREQ\n");
+		DBG("PlanB: IOCTL VIDIOCSFREQ\n");
 			goto unimplemented; }
 		case VIDIOCGFREQ: {
-		DEBUG("PlanB: IOCTL VIDIOCGFREQ\n");
+		DBG("PlanB: IOCTL VIDIOCGFREQ\n");
 			goto unimplemented; }
 		case VIDIOCKEY: {
-		DEBUG("PlanB: IOCTL VIDIOCKEY\n");
+		DBG("PlanB: IOCTL VIDIOCKEY\n");
 			goto unimplemented; }
 		case VIDIOCSAUDIO: {
-		DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n");
+		DBG("PlanB: IOCTL VIDIOCSAUDIO\n");
 			goto unimplemented; }
 		case VIDIOCGAUDIO: {
-		DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n");
+		DBG("PlanB: IOCTL VIDIOCGAUDIO\n");
 			goto unimplemented; }
 unimplemented:
-		DEBUG("       Unimplemented\n");
+		DBG("       Unimplemented\n");
 			return -ENOIOCTLCMD;
 	}
 	return 0;
@@ -1995,9 +2192,9 @@
 
 static int planb_mmap(struct video_device *dev, const char *adr, unsigned long size)
 {
-	int i;
-	struct planb *pb = (struct planb *)dev;
-        unsigned long start = (unsigned long)adr;
+	struct planb	*pb = (struct planb *)dev->priv;
+        unsigned long	start = (unsigned long)adr;
+	int		i;
 
 	if (size > MAX_GBUFFERS * PLANB_MAX_FBUF)
 	        return -EINVAL;
@@ -2006,7 +2203,7 @@
 		if((err=grabbuf_alloc(pb)))
 			return err;
 	}
-	for (i = 0; i < pb->rawbuf_size; i++) {
+	for (i = 0; i < pb->rawbuf_nchunks; i++) {
 		if (remap_page_range(start, virt_to_phys((void *)pb->rawbuf[i]),
 						PAGE_SIZE, PAGE_SHARED))
 			return -EAGAIN;
@@ -2018,77 +2215,223 @@
 	return 0;
 }
 
+/**********************************
+ * VBI device operation functions *
+ **********************************/
+
+static long planb_vbi_read(struct video_device *dev, char *buf,
+	unsigned long count, int nonblock)
+{
+	struct planb	*pb = (struct planb *)dev->priv;
+	int		q,todo;
+	DECLARE_WAITQUEUE(wait, current);
+
+/* Dummy for now */
+	printk ("PlanB: VBI read %li bytes.\n", count);
+	return (0);
+
+	todo=count;
+	while (todo && todo>(q=VBIBUF_SIZE-pb->vbip)) 
+	{
+		if(copy_to_user((void *) buf, (void *) pb->vbibuf+pb->vbip, q))
+			return -EFAULT;
+		todo-=q;
+		buf+=q;
+
+		add_wait_queue(&pb->vbiq, &wait);
+		current->state = TASK_INTERRUPTIBLE;
+		if (todo && q==VBIBUF_SIZE-pb->vbip) {
+			if(nonblock) {
+				remove_wait_queue(&pb->vbiq, &wait);
+				current->state = TASK_RUNNING;
+				if(count==todo)
+					return -EWOULDBLOCK;
+				return count-todo;
+			}
+			schedule();
+			if(signal_pending(current)) {
+				remove_wait_queue(&pb->vbiq, &wait);
+				current->state = TASK_RUNNING;
+				if(todo==count)
+					return -EINTR;
+				else
+					return count-todo;
+			}
+		}
+		remove_wait_queue(&pb->vbiq, &wait);
+		current->state = TASK_RUNNING;
+	}
+	if (todo) {
+		if(copy_to_user((void *) buf, (void *) pb->vbibuf+pb->vbip,
+		    todo))
+			return -EFAULT;
+		pb->vbip+=todo;
+	}
+	return count;
+}
+
+static unsigned int planb_vbi_poll(struct video_device *dev,
+	struct file *file, poll_table *wait)
+{
+	struct planb	*pb = (struct planb *)dev->priv;
+	unsigned int	mask = 0;
+
+	printk ("PlanB: VBI poll.\n");
+	poll_wait(file, &pb->vbiq, wait);
+
+	if (pb->vbip < VBIBUF_SIZE)
+		mask |= (POLLIN | POLLRDNORM);
+
+	return mask;
+}
+
+static int planb_vbi_open(struct video_device *dev, int flags)
+{
+	struct planb	*pb = (struct planb *)dev->priv;
+	int		err;
+
+	/* first open on the driver? */
+	if(pb->vid_user + pb->vbi_user == 0) {
+		if((err = planb_prepare_open(pb)) != 0)
+			return err;
+	}
+	/* first open on the vbi device? */
+	if(pb->vbi_user == 1) {
+		if((err = planb_prepare_vbi(pb)) != 0)
+			return err;
+	}
+	++pb->vbi_user;
+
+	DBG("PlanB: VBI open\n");
+
+	MOD_INC_USE_COUNT;
+	return 0;   
+}
+
+static void planb_vbi_close(struct video_device *dev)
+{
+	struct planb	*pb = (struct planb *)dev->priv;
+
+	/* last close on vbi device? */
+	if(--pb->vbi_user == 0) {
+		planb_close_vbi(pb);
+	}
+	/* last close on any planb device? */
+	if(pb->vid_user + pb->vbi_user == 0) {
+		planb_prepare_close(pb);
+	}
+
+	DBG("PlanB: VBI close\n");
+
+	MOD_DEC_USE_COUNT;  
+	return;
+}
+
+static int planb_vbi_ioctl(struct video_device *dev, unsigned int cmd,
+	void *arg)
+{
+	switch (cmd) {  
+		/* This is only for alevt */
+		case BTTV_VBISIZE:
+			DBG("PlanB: IOCTL BTTV_VBISIZE.\n");
+			return VBIBUF_SIZE;
+		default:
+			DBG("PlanB: Unimplemented VBI IOCTL no. %i.\n", cmd);
+			return -EINVAL;
+	}
+}
+
 static struct video_device planb_template=
 {
 	owner:		THIS_MODULE,
 	name:		PLANB_DEVICE_NAME,
-	type:		VID_TYPE_OVERLAY,
+	type:		VID_TYPE_CAPTURE|VID_TYPE_OVERLAY,
 	hardware:	VID_HARDWARE_PLANB,
 	open:		planb_open,
 	close:		planb_close,
 	read:		planb_read,
-	write:		planb_write,
+	write:		planb_write,	/* not implemented */
 	ioctl:		planb_ioctl,
 	mmap:		planb_mmap,	/* mmap? */
 };
 
-static int init_planb(struct planb *pb)
+static struct video_device planb_vbi_template=
+{
+	owner:		THIS_MODULE,
+	name:		PLANB_VBI_NAME,
+	type:		VID_TYPE_CAPTURE|VID_TYPE_TELETEXT,
+	hardware:	VID_HARDWARE_PLANB,
+	open:		planb_vbi_open,
+	close:		planb_vbi_close,
+	read:		planb_vbi_read,
+	write:		planb_write,	/* not implemented */
+	poll:		planb_vbi_poll,
+	ioctl:		planb_vbi_ioctl,
+};
+
+static int __devinit init_planb(struct planb *pb)
 {
 	unsigned char saa_rev;
 	int i, result;
 	unsigned long flags;
 
-	memset ((void *) &pb->win, 0, sizeof (struct planb_window));
+	printk(KERN_INFO "PlanB: PowerMacintosh video input driver rev. %s\n", PLANB_REV);
+
+	pb->video_dev.minor = -1;
+	pb->vid_user = 0;
+
 	/* Simple sanity check */
 	if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) {
 		printk(KERN_ERR "PlanB: Option(s) invalid\n");
 		return -2;
 	}
+	memset ((void *) &pb->win, 0, sizeof (struct planb_window));
 	pb->win.norm = def_norm;
 	pb->win.mode = PLANB_TV_MODE;	/* TV mode */
-	pb->win.interlace=1;
-	pb->win.x=0;
-	pb->win.y=0;
-	pb->win.width=768; /* 640 */
-	pb->win.height=576; /* 480 */
-	pb->maxlines=576;
-#if 0
-	btv->win.cropwidth=768; /* 640 */
-	btv->win.cropheight=576; /* 480 */
-	btv->win.cropx=0;
-	btv->win.cropy=0;
-#endif
-	pb->win.pad=0;
-	pb->win.bpp=4;
-	pb->win.depth=32;
-	pb->win.color_fmt=PLANB_COLOUR32;
-	pb->win.bpl=1024*pb->win.bpp;
-	pb->win.swidth=1024;
-	pb->win.sheight=768;
+	pb->win.interlace = 1;
+	pb->win.x = 0;
+	pb->win.y = 0;
+	pb->win.width = 768; /* 640 */
+	pb->win.height = 576; /* 480 */
+	pb->win.pad = 0;
+	pb->win.bpp = 4;
+	pb->win.depth = 32;
+	pb->win.color_fmt = VIDEO_PALETTE_RGB32;
+	pb->win.bpl = 1024 * pb->win.bpp;
+	pb->win.swidth = 1024;
+	pb->win.sheight = 768;
+	pb->maxlines = 576;
 #ifdef PLANB_GSCANLINE
 	if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE
 				|| (pb->gbytes_per_line <= 0))
 		return -3;
 	else {
 		/* page align pb->gbytes_per_line for DMA purpose */
-		for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);)
-			i>>=1;
+		for(i = PAGE_SIZE; pb->gbytes_per_line < (i >> 1);)
+			i >>= 1;
 		pb->gbytes_per_line = i;
 	}
 #endif
 	pb->tab_size = PLANB_MAXLINES + 40;
 	pb->suspend = 0;
 	init_MUTEX(&pb->lock);
-	pb->ch1_cmd = 0;
-	pb->ch2_cmd = 0;
+	pb->vid_cbo.start = 0;
+	pb->clip_cbo.start = 0;
 	pb->mask = 0;
-	pb->priv_space = 0;
-	pb->offset = 0;
-	pb->user = 0;
+	pb->vid_raw = 0;
 	pb->overlay = 0;
 	init_waitqueue_head(&pb->suspendq);
 	pb->cmd_buff_inited = 0;
-	pb->frame_buffer_phys = 0;
+	pb->fb.phys = 0;
+	pb->fb.offset = 0;
+
+	/* VBI stuff: */
+	pb->vbi_dev.minor = -1;
+	pb->vbi_user = 0;
+	pb->vbirunning = 0;
+	pb->vbip = 0;
+	pb->vbibuf = 0;
+	init_waitqueue_head(&pb->vbiq);
 
 	/* Reset DMA controllers */
 	planb_dbdma_stop(&pb->planb_base->ch2);
@@ -2117,9 +2460,6 @@
 	disable_irq(pb->irq);
 	restore_flags(flags);
         
-	/* Now add the template and register the device unit. */
-	memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
-
 	pb->picture.brightness=0x90<<8;
 	pb->picture.contrast = 0x70 << 8;
 	pb->picture.colour = 0x70<<8;
@@ -2127,170 +2467,131 @@
 	pb->picture.whiteness = 0;
 	pb->picture.depth = pb->win.depth;
 
-	pb->frame_stat=NULL;
 	init_waitqueue_head(&pb->capq);
 	for(i=0; i<MAX_GBUFFERS; i++) {
-		pb->gbuf_idx[i] = PLANB_MAX_FBUF * i / PAGE_SIZE;
-		pb->gwidth[i]=0;
-		pb->gheight[i]=0;
-		pb->gfmt[i]=0;
-		pb->cap_cmd[i]=NULL;
+		gbuf_ptr	gbuf = &pb->gbuf[i];
+
+		gbuf->idx = PLANB_MAX_FBUF * i / PAGE_SIZE;
+		gbuf->width=0;
+		gbuf->height=0;
+		gbuf->fmt=0;
+		gbuf->cap_cmd=NULL;
 #ifndef PLANB_GSCANLINE
-		pb->l_fr_addr_idx[i] = MAX_GBUFFERS * (PLANB_MAX_FBUF
+		gbuf->l_fr_addr_idx = MAX_GBUFFERS * (PLANB_MAX_FBUF
 						/ PAGE_SIZE + 1) + MAX_LNUM * i;
-		pb->lsize[i] = 0;
-		pb->lnum[i] = 0;
+		gbuf->lsize = 0;
+		gbuf->lnum = 0;
 #endif
 	}
 	pb->rawbuf=NULL;
 	pb->grabbing=0;
 
 	/* enable interrupts */
-	out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+	writel(PLANB_CLR_IRQ, &pb->planb_base->intr_stat);
 	pb->intr_mask = PLANB_FRM_IRQ;
 	enable_irq(pb->irq);
 
+	/* Now add the templates and register the device units. */
+	memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
+	pb->video_dev.priv = pb;
+	memcpy(&pb->vbi_dev,&planb_vbi_template,sizeof(planb_vbi_template));
+	
 	if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER, video_nr)<0)
 		return -1;
+	if(video_register_device(&pb->vbi_dev, VFL_TYPE_VBI, vbi_nr)<0) {
+		video_unregister_device(&pb->video_dev);
+		return -1;
+	}
 
 	return 0;
 }
 
 /*
- *	Scan for a PlanB controller, request the irq and map the io memory 
+ *	Scan for a PlanB controller and map the io memory 
  */
-
 static int find_planb(void)
 {
 	struct planb		*pb;
-	struct device_node	*planb_devices;
-	unsigned char		dev_fn, confreg, bus;
-	unsigned int		old_base, new_base;
-	unsigned int		irq;
-	struct pci_dev 		*pdev;
+	struct pci_dev 		*pdev = NULL;
+	unsigned long		base;
+	int			planb_num = 0;
 
 	if (_machine != _MACH_Pmac)
 		return 0;
 
-	planb_devices = find_devices("planb");
-	if (planb_devices == 0) {
-		planb_num=0;
+	pdev = pci_find_device(APPLE_VENDOR_ID, PLANB_DEV_ID, pdev);
+	if (pdev == NULL) {
 		printk(KERN_WARNING "PlanB: no device found!\n");
 		return planb_num;
 	}
 
-	if (planb_devices->next != NULL)
-		printk(KERN_ERR "Warning: only using first PlanB device!\n");
-	pb = &planbs[0];
+	pb = &planbs;
 	planb_num = 1;
+	base = pdev->resource[0].start;
 
-        if (planb_devices->n_addrs != 1) {
-                printk (KERN_WARNING "PlanB: expecting 1 address for planb "
-                	"(got %d)", planb_devices->n_addrs);
-		return 0;
-	}
-
-	if (planb_devices->n_intrs == 0) {
-		printk(KERN_WARNING "PlanB: no intrs for device %s\n",
-		       planb_devices->full_name);
-		return 0;
-	} else {
-		irq = planb_devices->intrs[0].line;
-	}
-
-	/* Initialize PlanB's PCI registers */
-
-	/* There is a bug with the way OF assigns addresses
-	   to the devices behind the chaos bridge.
-	   control needs only 0x1000 of space, but decodes only
-	   the upper 16 bits. It therefore occupies a full 64K.
-	   OF assigns the planb controller memory within this space;
-	   so we need to change that here in order to access planb. */
-
-	/* We remap to 0xf1000000 in hope that nobody uses it ! */
-
-	bus = (planb_devices->addrs[0].space >> 16) & 0xff;
-	dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff;
-	confreg = planb_devices->addrs[0].space & 0xff;
-	old_base = planb_devices->addrs[0].address;
-	new_base = 0xf1000000;
-
-	DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
-		"membase 0x%x (base reg. 0x%x)\n",
-		bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
-
-	pdev = pci_find_slot (bus, dev_fn);
-	if (!pdev) {
-		printk(KERN_ERR "cannot find slot\n");
-		/* XXX handle error */
-	}
+	DBG("PlanB: Found device %s, membase 0x%lx, irq %d\n",
+		pdev->slot_name, base, pdev->irq);
 
 	/* Enable response in memory space, bus mastering,
 	   use memory write and invalidate */
-	pci_write_config_word (pdev, PCI_COMMAND,
-		PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
-		PCI_COMMAND_INVALIDATE);
-	/* Set PCI Cache line size & latency timer */
-	pci_write_config_byte (pdev, PCI_CACHE_LINE_SIZE, 0x8);
+	pci_enable_device (pdev);
+	pci_set_master (pdev);
+	pci_set_mwi(pdev);
+	/* value copied from MacOS... */
 	pci_write_config_byte (pdev, PCI_LATENCY_TIMER, 0x40);
 
-	/* Set the new base address */
-	pci_write_config_dword (pdev, confreg, new_base);
-
 	planb_regs = (volatile struct planb_registers *)
-						ioremap (new_base, 0x400);
+						ioremap (base, 0x400);
 	pb->planb_base = planb_regs;
-	pb->planb_base_phys = (struct planb_registers *)new_base;
-	pb->irq	= irq;
+	pb->planb_base_bus = (struct planb_registers *)base;
+	pb->irq	= pdev->irq;
 	
 	return planb_num;
 }
 
 static void release_planb(void)
 {
-	int i;
 	struct planb *pb;
 
-	for (i=0;i<planb_num; i++) 
-	{
-		pb=&planbs[i];
+	pb=&planbs;
 
-		/* stop and flash DMAs unconditionally */
-		planb_dbdma_stop(&pb->planb_base->ch2);
-		planb_dbdma_stop(&pb->planb_base->ch1);
+	/* stop and flush DMAs unconditionally */
+	planb_dbdma_stop(&pb->planb_base->ch2);
+	planb_dbdma_stop(&pb->planb_base->ch1);
 
-		/* clear and free interrupts */
-		pb->intr_mask = PLANB_CLR_IRQ;
-		out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
-		free_irq(pb->irq, pb);
+	/* clear and free interrupts */
+	pb->intr_mask = PLANB_CLR_IRQ;
+	writel(PLANB_CLR_IRQ, &pb->planb_base->intr_stat);
+	free_irq(pb->irq, pb);
 
-		/* make sure all allocated memory are freed */
-		planb_prepare_close(pb);
+	/* make sure all allocated memory are freed */
+	planb_prepare_close(pb);
 
-		printk(KERN_INFO "PlanB: unregistering with v4l\n");
-		video_unregister_device(&pb->video_dev);
+	printk(KERN_INFO "PlanB: unregistering with v4l\n");
+	video_unregister_device(&pb->video_dev);
+	video_unregister_device(&pb->vbi_dev);
 
-		/* note that iounmap() does nothing on the PPC right now */
-		iounmap ((void *)pb->planb_base);
-	}
+	/* note that iounmap() does nothing on the PPC right now */
+	iounmap ((void *)pb->planb_base);
 }
 
 static int __init init_planbs(void)
 {
-	int i;
-  
-	if (find_planb()<=0)
+	int planb_num;
+
+	planb_num=find_planb();
+
+	if (planb_num < 0)
 		return -EIO;
+	if (planb_num == 0)
+		return -ENXIO;
 
-	for (i=0; i<planb_num; i++) {
-		if (init_planb(&planbs[i])<0) {
-			printk(KERN_ERR "PlanB: error registering device %d"
-							" with v4l\n", i);
-			release_planb();
-			return -EIO;
-		} 
-		printk(KERN_INFO "PlanB: registered device %d with v4l\n", i);
-	}  
+	if (init_planb(&planbs) < 0) {
+		printk(KERN_ERR "PlanB: error registering planb device"
+						" with v4l\n");
+		release_planb();
+		return -EIO;
+	} 
 	return 0;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)