patch-2.4.7 linux/drivers/media/video/zr36067.c

Next file: linux/drivers/media/video/zr36120.c
Previous file: linux/drivers/media/video/zr36060.h
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.6/linux/drivers/media/video/zr36067.c linux/drivers/media/video/zr36067.c
@@ -0,0 +1,4900 @@
+#define DEBUGLEVEL 0
+#define MAX_KMALLOC_MEM (128*1024)
+
+/*
+   Miro/Pinnacle Systems Inc. DC10/DC10plus and
+   Linux Media Labs LML33 video capture boards driver
+   now with IOMega BUZ support!
+   
+   Copyright (C) 2000 Serguei Miridonov <mirsev@cicese.mx>
+
+   Changes for BUZ by Wolfgang Scherr <scherr@net4you.net>
+   
+   Based on
+   
+    Miro DC10 driver
+    Copyright (C) 1999 Wolfgang Scherr <scherr@net4you.net>
+   
+    Iomega Buz driver version 1.0
+    Copyright (C) 1999 Rainer Johanni <Rainer@Johanni.de>
+
+    buz.0.0.3
+    Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+
+    bttv - Bt848 frame grabber driver
+    Copyright (C) 1996,97,98 Ralph  Metzler (rjkm@thp.uni-koeln.de)
+                           & Marcus Metzler (mocm@thp.uni-koeln.de)
+
+    
+    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
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/pci.h>
+#include <linux/signal.h>
+#include <asm/io.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <linux/sched.h>
+#include <asm/segment.h>
+#include <linux/types.h>
+#include <linux/wrapper.h>
+
+#include <linux/spinlock.h>
+#include <linux/vmalloc.h>
+#include <linux/i2c-old.h>
+#define     MAP_NR(x)       virt_to_page(x)
+#define     ZORAN_HARDWARE  VID_HARDWARE_ZR36067
+
+#include <linux/videodev.h>
+
+#include <asm/uaccess.h>
+#include <linux/proc_fs.h>
+
+#include "zoran.h"
+#include <linux/video_decoder.h>
+#include <linux/video_encoder.h>
+
+// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined
+#if !defined(CONFIG_BIGPHYS_AREA)
+//#undef CONFIG_BIGPHYS_AREA
+#define BUZ_USE_HIMEM
+#endif
+
+#if defined(CONFIG_BIGPHYS_AREA)
+#   include <linux/bigphysarea.h>
+#endif
+
+#define IRQ_MASK ( ZR36057_ISR_GIRQ0 | ZR36057_ISR_GIRQ1 | /* ZR36057_ISR_CodRepIRQ | */  ZR36057_ISR_JPEGRepIRQ )	// SW
+//#define GPIO_MASK 0xcd
+//#define GPIO_MASK 0x8d
+
+/*
+DC10:
+GPIO0 = /RESET ZR 36060
+GPIO1 = VIDEO BUS DIRECTION (0: CAPTURE, 1: DISPLAY)
+GPIO2 = VIDEO BUS ENABLE    (0: ON, 1: OFF)
+GPIO3 = /SLEEP ZR 36060
+GPIO4 = ADC7175 (video out) FREQUENCY (0: LCC/SAA7110, 1: 27 MHz (quarz))
+GPIO5 = ZORAN FREQUENCY  (0: LCC and LCC2 from SAA7110,
+                          1: 27 and 13.5 MHz (quarz))
+GPIO6 = /FRAME ZR 36060
+GPIO7 = /RESET ADV7175 (video out)
+(I think they lost the SAA7110 reset.....)
+
+GIRQ0 signals that ZR36060's DATERR# line is asserted.
+GIRQ1 signals a vertical sync of the video signal (VS SAA7110)
+
+  SAA7110A:
+   mode 0 - Composite
+   mode 1 - 
+   mode 2 - 
+   mode 3 - 
+   mode 4 - 
+   mode 5 - internal Composite (from PCTV)
+   mode 6 - 
+   mode 7 - S-Video
+
+BUZ:
+GPIO0 = 1, take board out of reset
+GPIO1 = 1, take JPEG codec out of sleep mode
+GPIO3 = 1, deassert FRAME# to 36060
+
+GIRQ0 signals a vertical sync of the video signal
+GIRQ1 signals that ZR36060's DATERR# line is asserted.
+
+SAA7111A
+
+   In their infinite wisdom, the Iomega engineers decided to
+   use the same input line for composite and S-Video Color,
+   although there are two entries not connected at all!
+   Through this ingenious strike, it is not possible to
+   keep two running video sources connected at the same time
+   to Composite and S-VHS input!
+
+   mode 0 - N/C
+   mode 1 - S-Video Y
+   mode 2 - noise or something I don't know
+   mode 3 - Composite and S-Video C
+   mode 4 - N/C
+   mode 5 - S-Video (gain C independently selectable of gain Y)
+   mode 6 - N/C
+   mode 7 - S-Video (gain C adapted to gain Y)
+*/
+
+#define MAJOR_VERSION 0		/* driver major version */
+#define MINOR_VERSION 7		/* driver minor version */
+
+#define ZORAN_NAME    "zr36067"	/* name of the device */
+
+#define BUZ_ERR       KERN_ERR     ZORAN_NAME
+#define BUZ_DEBUG     KERN_INFO    ZORAN_NAME
+#define BUZ_INFO      KERN_INFO    ZORAN_NAME
+#define BUZ_WARNING   KERN_WARNING ZORAN_NAME
+
+#if(DEBUGLEVEL>0)
+#define DEBUG1(x...)    x
+#else
+#define DEBUG1(x...)
+#endif
+
+#if(DEBUGLEVEL>1)
+#define DEBUG2(x...)    x
+#else
+#define DEBUG2(x...)
+#endif
+
+#if(DEBUGLEVEL>2)
+#define DEBUG3(x...)    x
+#else
+#define DEBUG3(x...)
+#endif
+
+#if(DEBUGLEVEL>3)
+#define DEBUG4(x...)    x
+#else
+#define DEBUG4(x...)
+#endif
+
+/* The parameters for this driver */
+
+/*
+   The video mem address of the video card.
+   The driver has a little database for some videocards
+   to determine it from there. If your video card is not in there
+   you have either to give it to the driver as a parameter
+   or set in in a VIDIOCSFBUF ioctl
+ */
+
+static unsigned long vidmem = 0;	/* Video memory base address */
+
+/* Special purposes only: */
+
+static int triton = 0;		/* 0=no, 1=yes */
+static int natoma = 0;		/* 0=no, 1=yes */
+
+/*
+   Number and size of grab buffers for Video 4 Linux
+   The vast majority of applications should not need more than 2,
+   the very popular BTTV driver actually does ONLY have 2.
+   Time sensitive applications might need more, the maximum
+   is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
+
+   The size is set so that the maximum possible request
+   can be satisfied. Decrease  it, if bigphys_area alloc'd
+   memory is low. If you don't have the bigphys_area patch,
+   set it to 128 KB. Will you allow only to grab small
+   images with V4L, but that's better than nothing.
+
+   v4l_bufsize has to be given in KB !
+
+*/
+
+static int v4l_nbufs = 2;
+static int v4l_bufsize = 128;	/* Everybody should be able to work with this setting */
+
+/*
+   Default input and video norm at startup of the driver.
+*/
+
+static int default_input = 0;	/* 0=Composite, 1=S-VHS */
+static int default_norm = 0;	/* 0=PAL, 1=NTSC 2=SECAM */
+static int lock_norm = 0;	/* 1=Don't change TV standard (norm) */
+
+static int pass_through = 0;	/* 1=Pass through TV signal when device is not used */
+				/* 0=Show color bar when device is not used (LML33: only if lml33dpath=1) */
+
+static int lml33dpath = 0;	/* 1 will use digital path in capture mode instead of analog.
+				   It can be used for picture adjustments using tool like xawtv
+				   while watching image on TV monitor connected to the output.
+				   However, due to absence of 75 Ohm load on Bt819 input, there
+				   will be some image imperfections */
+static int video_nr = -1;
+
+MODULE_PARM(vidmem, "i");
+MODULE_PARM(triton, "i");
+MODULE_PARM(natoma, "i");
+MODULE_PARM(v4l_nbufs, "i");
+MODULE_PARM(v4l_bufsize, "i");
+MODULE_PARM(default_input, "i");
+MODULE_PARM(default_norm, "i");
+MODULE_PARM(lock_norm, "i");
+MODULE_PARM(pass_through, "i");
+MODULE_PARM(lml33dpath, "i");
+MODULE_PARM(video_nr, "i");
+
+/* Anybody who uses more than four? */
+#define BUZ_MAX 4
+
+static int zoran_num;		/* number of Buzs in use */
+static struct zoran zoran[BUZ_MAX];
+
+/* forward references */
+
+static void v4l_fbuffer_free(struct zoran *zr);
+static void jpg_fbuffer_free(struct zoran *zr);
+static void zoran_feed_stat_com(struct zoran *zr);
+
+/*
+ *   Allocate the V4L grab buffers
+ *
+ *   These have to be pysically contiguous.
+ *   If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc
+ *   else we try to allocate them with bigphysarea_alloc_pages
+ *   if the bigphysarea patch is present in the kernel,
+ *   else we try to use high memory (if the user has bootet
+ *   Linux with the necessary memory left over).
+ */
+
+static int v4l_fbuffer_alloc(struct zoran *zr)
+{
+	int i, off;
+	unsigned char *mem;
+
+	for (i = 0; i < v4l_nbufs; i++) {
+		if (zr->v4l_gbuf[i].fbuffer)
+			printk(KERN_WARNING
+			       "%s: v4l_fbuffer_alloc: buffer %d allready allocated ???\n",
+			       zr->name, i);
+
+		//udelay(20);
+		if (v4l_bufsize <= MAX_KMALLOC_MEM) {
+			/* Use kmalloc */
+
+			mem =
+			    (unsigned char *) kmalloc(v4l_bufsize,
+						      GFP_KERNEL);
+			if (mem == 0) {
+				printk(KERN_ERR
+				       "%s: kmalloc for V4L bufs failed\n",
+				       zr->name);
+				v4l_fbuffer_free(zr);
+				return -ENOBUFS;
+			}
+			zr->v4l_gbuf[i].fbuffer = mem;
+			zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem);
+			zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem);
+			for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
+				mem_map_reserve(MAP_NR(mem + off));
+			DEBUG1(printk
+			       (KERN_INFO
+				"%s: V4L frame %d mem 0x%lx (bus: 0x%lx)\n",
+				zr->name, i, (unsigned long) mem,
+				virt_to_bus(mem)));
+		} else {
+#if defined(CONFIG_BIGPHYS_AREA)
+			/* Use bigphysarea_alloc_pages */
+
+			int n = (v4l_bufsize + PAGE_SIZE - 1) / PAGE_SIZE;
+			mem =
+			    (unsigned char *) bigphysarea_alloc_pages(n, 0,
+								      GFP_KERNEL);
+			if (mem == 0) {
+				printk(KERN_ERR
+				       "%s: bigphysarea_alloc_pages for V4L bufs failed\n",
+				       zr->name);
+				v4l_fbuffer_free(zr);
+				return -ENOBUFS;
+			}
+			zr->v4l_gbuf[i].fbuffer = mem;
+			zr->v4l_gbuf[i].fbuffer_phys = virt_to_phys(mem);
+			zr->v4l_gbuf[i].fbuffer_bus = virt_to_bus(mem);
+			DEBUG1(printk
+			       (KERN_INFO
+				"%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n",
+				zr->name, i, (unsigned) mem,
+				(unsigned) virt_to_bus(mem)));
+
+			/* Zero out the allocated memory */
+			memset(zr->v4l_gbuf[i].fbuffer, 0, v4l_bufsize);
+#else
+			/* No bigphysarea present, usage of high memory disabled,
+			   but user wants buffers of more than MAX_KMALLOC_MEM */
+			printk(KERN_ERR
+			       "%s: No bigphysarea_patch present, usage of high memory disabled,\n",
+			       zr->name);
+			printk(KERN_ERR
+			       "%s: sorry, could not allocate V4L buffers of size %d KB.\n",
+			       zr->name, v4l_bufsize >> 10);
+			return -ENOBUFS;
+#endif
+		}
+	}
+
+	return 0;
+}
+
+/* free the V4L grab buffers */
+
+static void v4l_fbuffer_free(struct zoran *zr)
+{
+	int i, off;
+	unsigned char *mem;
+
+	for (i = 0; i < v4l_nbufs; i++) {
+		if (!zr->v4l_gbuf[i].fbuffer)
+			continue;
+
+		if (v4l_bufsize <= MAX_KMALLOC_MEM) {
+			mem = zr->v4l_gbuf[i].fbuffer;
+			for (off = 0; off < v4l_bufsize; off += PAGE_SIZE)
+				mem_map_unreserve(MAP_NR(mem + off));
+			kfree((void *) zr->v4l_gbuf[i].fbuffer);
+		}
+#if defined(CONFIG_BIGPHYS_AREA)
+		else
+			bigphysarea_free_pages((void *) zr->v4l_gbuf[i].
+					       fbuffer);
+#endif
+		zr->v4l_gbuf[i].fbuffer = NULL;
+	}
+}
+
+/*
+ *   Allocate the MJPEG grab buffers.
+ *
+ *   If the requested buffer size is smaller than MAX_KMALLOC_MEM,
+ *   kmalloc is used to request a physically contiguous area,
+ *   else we allocate the memory in framgents with get_free_page.
+ *
+ *   If a Natoma chipset is present and this is a revision 1 zr36057,
+ *   each MJPEG buffer needs to be physically contiguous.
+ *   (RJ: This statement is from Dave Perks' original driver,
+ *   I could never check it because I have a zr36067)
+ *   The driver cares about this because it reduces the buffer
+ *   size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation).
+ *
+ *   RJ: The contents grab buffers needs never be accessed in the driver.
+ *       Therefore there is no need to allocate them with vmalloc in order
+ *       to get a contiguous virtual memory space.
+ *       I don't understand why many other drivers first allocate them with
+ *       vmalloc (which uses internally also get_free_page, but delivers you
+ *       virtual addresses) and then again have to make a lot of efforts
+ *       to get the physical address.
+ *
+ */
+
+static int jpg_fbuffer_alloc(struct zoran *zr)
+{
+	int i, j, off;		//alloc_contig;
+	unsigned long mem;
+
+	/* Decide if we should alloc contiguous or fragmented memory */
+	/* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
+
+	//alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM);
+
+	for (i = 0; i < zr->jpg_nbufs; i++) {
+		if (zr->jpg_gbuf[i].frag_tab)
+			printk(KERN_WARNING
+			       "%s: jpg_fbuffer_alloc: buffer %d allready allocated ???\n",
+			       zr->name, i);
+
+		/* Allocate fragment table for this buffer */
+
+		mem = get_free_page(GFP_KERNEL);
+		if (mem == 0) {
+			printk(KERN_ERR
+			       "%s: jpg_fbuffer_alloc: get_free_page (frag_tab) failed for buffer %d\n",
+			       zr->name, i);
+			jpg_fbuffer_free(zr);
+			return -ENOBUFS;
+		}
+		memset((void *) mem, 0, PAGE_SIZE);
+		zr->jpg_gbuf[i].frag_tab = (u32 *) mem;
+		zr->jpg_gbuf[i].frag_tab_bus = virt_to_bus((void *) mem);
+
+		//if (alloc_contig) {
+		if (zr->need_contiguous) {
+			mem = (unsigned long) kmalloc(zr->jpg_bufsize, GFP_KERNEL);
+			if (mem == 0) {
+				printk(KERN_ERR "%s: jpg_fbuffer_alloc: kmalloc failed for buffer %d\n",
+				       zr->name, i);
+				jpg_fbuffer_free(zr);
+				return -ENOBUFS;
+			}
+			zr->jpg_gbuf[i].frag_tab[0] = virt_to_bus((void *) mem);
+			zr->jpg_gbuf[i].frag_tab[1] =
+			    ((zr->jpg_bufsize / 4) << 1) | 1;
+			for (off = 0; off < zr->jpg_bufsize; off += PAGE_SIZE)
+				mem_map_reserve(MAP_NR(mem + off));
+		} else {
+			/* jpg_bufsize is allreay page aligned */
+			for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) 
+			{
+				mem = get_free_page(GFP_KERNEL);
+				if (mem == 0) {
+					printk(KERN_ERR
+					       "%s: jpg_fbuffer_alloc: get_free_page failed for buffer %d\n",
+					       zr->name, i);
+					jpg_fbuffer_free(zr);
+					return -ENOBUFS;
+				}
+
+				zr->jpg_gbuf[i].frag_tab[2 * j] =
+				    virt_to_bus((void *) mem);
+				zr->jpg_gbuf[i].frag_tab[2 * j + 1] =
+				    (PAGE_SIZE / 4) << 1;
+				mem_map_reserve(MAP_NR(mem));
+			}
+
+			zr->jpg_gbuf[i].frag_tab[2 * j - 1] |= 1;
+		}
+	}
+
+	DEBUG1(printk
+	       ("%s: jpg_fbuffer_alloc: %ld KB allocated\n", zr->name,
+		(zr->jpg_nbufs * zr->jpg_bufsize) >> 10));
+	zr->jpg_buffers_allocated = 1;
+	return 0;
+}
+
+/* free the MJPEG grab buffers */
+static void jpg_fbuffer_free(struct zoran *zr)
+{
+	int i, j, off;		// alloc_contig;
+	unsigned char *mem;
+
+	/* Decide if we should alloc contiguous or fragmented memory */
+	/* This has to be identical in jpg_fbuffer_alloc and jpg_fbuffer_free */
+
+	//alloc_contig = (zr->jpg_bufsize <= MAX_KMALLOC_MEM);
+
+	for (i = 0; i < zr->jpg_nbufs; i++) {
+		if (!zr->jpg_gbuf[i].frag_tab)
+			continue;
+
+		//if (alloc_contig) {
+		if (zr->need_contiguous) {
+			if (zr->jpg_gbuf[i].frag_tab[0]) {
+				mem =
+				    (unsigned char *) bus_to_virt(zr->
+								  jpg_gbuf
+								  [i].
+								  frag_tab
+								  [0]);
+				for (off = 0; off < zr->jpg_bufsize;
+				     off += PAGE_SIZE)
+					mem_map_unreserve(MAP_NR
+							  (mem + off));
+				kfree((void *) mem);
+				zr->jpg_gbuf[i].frag_tab[0] = 0;
+				zr->jpg_gbuf[i].frag_tab[1] = 0;
+			}
+		} else {
+			for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
+				if (!zr->jpg_gbuf[i].frag_tab[2 * j])
+					break;
+				mem_map_unreserve(MAP_NR
+						  (bus_to_virt
+						   (zr->jpg_gbuf[i].
+						    frag_tab[2 * j])));
+				free_page((unsigned long)
+					  bus_to_virt(zr->jpg_gbuf[i].
+						      frag_tab[2 * j]));
+				zr->jpg_gbuf[i].frag_tab[2 * j] = 0;
+				zr->jpg_gbuf[i].frag_tab[2 * j + 1] = 0;
+			}
+		}
+
+		free_page((unsigned long) zr->jpg_gbuf[i].frag_tab);
+		zr->jpg_gbuf[i].frag_tab = NULL;
+	}
+	zr->jpg_buffers_allocated = 0;
+}
+
+
+/* ----------------------------------------------------------------------- */
+
+/* I2C functions                                                           */
+
+#define I2C_DELAY   10
+
+
+/* software I2C functions */
+
+static void i2c_setlines(struct i2c_bus *bus, int ctrl, int data)
+{
+	struct zoran *zr = (struct zoran *) bus->data;
+	btwrite((data << 1) | ctrl, ZR36057_I2CBR);
+	udelay(I2C_DELAY);
+}
+
+static int i2c_getdataline(struct i2c_bus *bus)
+{
+	struct zoran *zr = (struct zoran *) bus->data;
+	return (btread(ZR36057_I2CBR) >> 1) & 1;
+}
+
+static void attach_inform(struct i2c_bus *bus, int id)
+{
+	int i;
+	struct zoran *zr = (struct zoran *) bus->data;
+
+	DEBUG1(printk(KERN_DEBUG "%s: i2c attach %02x\n", zr->name, id));
+	for (i = 0; i < bus->devcount; i++) {
+		if (strcmp(bus->devices[i]->name, "saa7110") == 0) {
+			if (zr->revision < 2) {
+				zr->card = DC10;
+				sprintf(zr->name, "DC10[%u]", zr->id);
+			} else {
+				zr->card = DC10plus;
+				sprintf(zr->name, "DC10plus[%u]", zr->id);
+			}
+			break;
+		}
+		if (strcmp(bus->devices[i]->name, "bt819") == 0) {
+			zr->card = LML33;
+			sprintf(zr->name, "LML33[%u]", zr->id);
+			break;
+		}
+		if (strcmp(bus->devices[i]->name, "saa7111") == 0) {
+			zr->card = BUZ;
+			sprintf(zr->name, "Buz[%u]", zr->id);
+			break;
+		}
+	}
+}
+
+static void detach_inform(struct i2c_bus *bus, int id)
+{
+	DEBUG1(struct zoran *zr = (struct zoran *) bus->data);
+	DEBUG1(printk(KERN_DEBUG "%s: i2c detach %02x\n", zr->name, id));
+}
+
+static struct i2c_bus zoran_i2c_bus_template = {
+	"zr36057",
+	I2C_BUSID_BT848,
+	NULL,
+
+	SPIN_LOCK_UNLOCKED,
+
+	attach_inform,
+	detach_inform,
+
+	i2c_setlines,
+	i2c_getdataline,
+	NULL,
+	NULL,
+};
+
+/*
+ *   Set the registers for the size we have specified. Don't bother
+ *   trying to understand this without the ZR36057 manual in front of
+ *   you [AC].
+ *
+ *   PS: The manual is free for download in .pdf format from
+ *   www.zoran.com - nicely done those folks.
+ */
+
+static struct tvnorm f50sqpixel = { 944, 768, 83, 880, 625, 576, 16 };
+static struct tvnorm f60sqpixel = { 780, 640, 51, 716, 525, 480, 12 };
+
+static struct tvnorm f50ccir601 = { 864, 720, 75, 804, 625, 576, 18 };
+static struct tvnorm f60ccir601 = { 858, 720, 57, 788, 525, 480, 16 };
+
+static struct tvnorm *dc10norms[] = {
+	&f50sqpixel,		/* PAL-BDGHI */
+	&f60sqpixel,		/* NTSC */
+	&f50sqpixel,		/* SECAM */
+};
+
+static struct tvnorm *lml33norms[] = {
+	&f50ccir601,		/* PAL-BDGHI */
+	&f60ccir601,		/* NTSC      */
+	NULL,			/* SECAM (not supported in LML33) */
+};
+
+static struct tvnorm *buznorms[] = {
+	&f50ccir601,		/* PAL-BDGHI */
+	&f60ccir601,		/* NTSC      */
+	NULL,			/* SECAM     */
+};
+
+static struct tvnorm *unsupported[] = {
+	NULL,			/* PAL-BDGHI */
+	NULL,			/* NTSC      */
+	NULL,			/* SECAM     */
+};
+
+static struct tvnorm **cardnorms[] = {
+	unsupported,		/* UNKNOWN  */
+	dc10norms,		/* DC10     */
+	dc10norms,		/* DC10plus */
+	lml33norms,		/* LML33    */
+	buznorms,		/* Buz      */
+};
+
+static u32 cardvsync[] = {
+	0,			/* UNKNOWN  */
+	ZR36057_ISR_GIRQ1,	/* DC10     */
+	ZR36057_ISR_GIRQ1,	/* DC10plus */
+	ZR36057_ISR_GIRQ0,	/* LML33    */
+	ZR36057_ISR_GIRQ0,	/* Buz      */
+};
+
+static u32 cardjpegint[] = {
+	0,			/* UNKNOWN  */
+	ZR36057_ISR_GIRQ0,	/* DC10     */
+	ZR36057_ISR_GIRQ0,	/* DC10plus */
+	ZR36057_ISR_GIRQ1,	/* LML33    */
+	ZR36057_ISR_GIRQ1,	/* Buz      */
+};
+
+static int format2bpp(int format)
+{
+	int bpp;
+
+	/* Determine the number of bytes per pixel for the video format requested */
+
+	switch (format) {
+
+	case VIDEO_PALETTE_YUV422:
+		bpp = 2;
+		break;
+
+	case VIDEO_PALETTE_RGB555:
+		bpp = 2;
+		break;
+
+	case VIDEO_PALETTE_RGB565:
+		bpp = 2;
+		break;
+
+	case VIDEO_PALETTE_RGB24:
+		bpp = 3;
+		break;
+
+	case VIDEO_PALETTE_RGB32:
+		bpp = 4;
+		break;
+
+	default:
+		bpp = 0;
+	}
+
+	return bpp;
+}
+
+static void zr36057_adjust_vfe(struct zoran *zr,
+			       enum zoran_codec_mode mode)
+{
+	u32 reg;
+	switch (mode) {
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		reg = btread(ZR36057_VFEHCR);
+		if (reg & (1 << 10)) {
+			reg += ((1 << 10) | 1);
+		}
+		btwrite(reg, ZR36057_VFEHCR);
+		break;
+	case BUZ_MODE_MOTION_COMPRESS:
+	case BUZ_MODE_IDLE:
+	default:
+		if (zr->params.norm == VIDEO_MODE_NTSC)
+			btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		else
+			btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+		reg = btread(ZR36057_VFEHCR);
+		if (!(reg & (1 << 10))) {
+			reg -= ((1 << 10) | 1);
+		}
+		btwrite(reg, ZR36057_VFEHCR);
+		break;
+	}
+}
+
+/*
+ * set geometry
+ */
+static void zr36057_set_vfe(struct zoran *zr, int video_width,
+			    int video_height, unsigned int video_format)
+{
+	struct tvnorm *tvn;
+	unsigned HStart, HEnd, VStart, VEnd;
+	unsigned DispMode;
+	unsigned VidWinWid, VidWinHt;
+	unsigned hcrop1, hcrop2, vcrop1, vcrop2;
+	unsigned Wa, We, Ha, He;
+	unsigned X, Y, HorDcm, VerDcm;
+	u32 reg;
+	unsigned mask_line_size;
+
+	tvn = zr->timing;
+
+	Wa = tvn->Wa;
+	Ha = tvn->Ha;
+
+	DEBUG1(printk (BUZ_INFO ": width = %d, height = %d\n", video_width, video_height));
+
+	if (zr->params.norm != VIDEO_MODE_PAL
+	    && zr->params.norm != VIDEO_MODE_NTSC
+	    && zr->params.norm != VIDEO_MODE_SECAM) {
+		printk(KERN_ERR "%s: set_vfe: video_norm = %d not valid\n",
+		       zr->name, zr->params.norm);
+		return;
+	}
+	if (video_width < BUZ_MIN_WIDTH || video_height < BUZ_MIN_HEIGHT
+	    || video_width > Wa || video_height > Ha) {
+		printk(KERN_ERR "%s: set_vfe: w=%d h=%d not valid\n",
+		       zr->name, video_width, video_height);
+		return;
+	}
+
+	/* if window has more than half of active height,
+	   switch on interlacing - we want the full information */
+
+	zr->video_interlace = (video_height > Ha / 2);
+
+	/**** zr36057 ****/
+
+	/* horizontal */
+	VidWinWid = video_width;
+	X = (VidWinWid * 64 + tvn->Wa - 1) / tvn->Wa;
+	We = (VidWinWid * 64) / X;
+	HorDcm = 64 - X;
+	hcrop1 = 2 * ((tvn->Wa - We) / 4);
+	hcrop2 = tvn->Wa - We - hcrop1;
+	HStart = tvn->HStart | 1;
+	if (zr->card == LML33)
+		HStart += 62;
+	if (zr->card == BUZ) {	//HStart += 67;
+		HStart += 44;
+	}
+	HEnd = HStart + tvn->Wa - 1;
+	HStart += hcrop1;
+	HEnd -= hcrop2;
+	reg = ((HStart & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HStart)
+	    | ((HEnd & ZR36057_VFEHCR_Hmask) << ZR36057_VFEHCR_HEnd);
+	if (zr->card != BUZ)
+		reg |= ZR36057_VFEHCR_HSPol;
+	btwrite(reg, ZR36057_VFEHCR);
+
+	/* Vertical */
+	DispMode = !zr->video_interlace;
+	VidWinHt = DispMode ? video_height : video_height / 2;
+	Y = (VidWinHt * 64 * 2 + tvn->Ha - 1) / tvn->Ha;
+	He = (VidWinHt * 64) / Y;
+	VerDcm = 64 - Y;
+	vcrop1 = (tvn->Ha / 2 - He) / 2;
+	vcrop2 = tvn->Ha / 2 - He - vcrop1;
+	VStart = tvn->VStart;
+	VEnd = VStart + tvn->Ha / 2 - 1;
+	VStart += vcrop1;
+	VEnd -= vcrop2;
+	reg = ((VStart & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VStart)
+	    | ((VEnd & ZR36057_VFEVCR_Vmask) << ZR36057_VFEVCR_VEnd);
+	reg |= ZR36057_VFEVCR_VSPol;
+	btwrite(reg, ZR36057_VFEVCR);
+
+	/* scaler and pixel format */
+	reg = 0;
+	reg |= (HorDcm << ZR36057_VFESPFR_HorDcm);
+	reg |= (VerDcm << ZR36057_VFESPFR_VerDcm);
+	reg |= (DispMode << ZR36057_VFESPFR_DispMode);
+	reg |= ZR36057_VFESPFR_LittleEndian;
+	/* RJ: I don't know, why the following has to be the opposite
+	   of the corresponding ZR36060 setting, but only this way
+	   we get the correct colors when uncompressing to the screen  */
+	//reg |= ZR36057_VFESPFR_VCLKPol; /**/
+	/* RJ: Don't know if that is needed for NTSC also */
+	if (zr->params.norm != VIDEO_MODE_NTSC)
+		reg |= ZR36057_VFESPFR_ExtFl;	// NEEDED!!!!!!! Wolfgang
+	reg |= ZR36057_VFESPFR_TopField;
+	switch (video_format) {
+
+	case VIDEO_PALETTE_YUV422:
+		reg |= ZR36057_VFESPFR_YUV422;
+		break;
+
+	case VIDEO_PALETTE_RGB555:
+		reg |= ZR36057_VFESPFR_RGB555 | ZR36057_VFESPFR_ErrDif;
+		break;
+
+	case VIDEO_PALETTE_RGB565:
+		reg |= ZR36057_VFESPFR_RGB565 | ZR36057_VFESPFR_ErrDif;
+		break;
+
+	case VIDEO_PALETTE_RGB24:
+		reg |= ZR36057_VFESPFR_RGB888 | ZR36057_VFESPFR_Pack24;
+		break;
+
+	case VIDEO_PALETTE_RGB32:
+		reg |= ZR36057_VFESPFR_RGB888;
+		break;
+
+	default:
+		printk(KERN_INFO "%s: Unknown color_fmt=%x\n", zr->name,
+		       video_format);
+		return;
+
+	}
+	if (HorDcm >= 48) {
+		reg |= 3 << ZR36057_VFESPFR_HFilter;	/* 5 tap filter */
+	} else if (HorDcm >= 32) {
+		reg |= 2 << ZR36057_VFESPFR_HFilter;	/* 4 tap filter */
+	} else if (HorDcm >= 16) {
+		reg |= 1 << ZR36057_VFESPFR_HFilter;	/* 3 tap filter */
+	}
+	btwrite(reg, ZR36057_VFESPFR);
+
+	/* display configuration */
+
+	reg = (16 << ZR36057_VDCR_MinPix)
+	    | (VidWinHt << ZR36057_VDCR_VidWinHt)
+	    | (VidWinWid << ZR36057_VDCR_VidWinWid);
+	if (triton || zr->revision <= 1)
+		reg &= ~ZR36057_VDCR_Triton;
+	else
+		reg |= ZR36057_VDCR_Triton;
+	btwrite(reg, ZR36057_VDCR);
+
+	/* Write overlay clipping mask data, but don't enable overlay clipping */
+	/* RJ: since this makes only sense on the screen, we use 
+	   zr->window.width instead of video_width */
+
+	mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+	reg = virt_to_bus(zr->overlay_mask);
+	btwrite(reg, ZR36057_MMTR);
+	reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+	btwrite(reg, ZR36057_MMBR);
+	reg = mask_line_size - (zr->window.width + 31) / 32;
+	if (DispMode == 0)
+		reg += mask_line_size;
+	reg <<= ZR36057_OCR_MaskStride;
+	btwrite(reg, ZR36057_OCR);
+
+	zr36057_adjust_vfe(zr, zr->codec_mode);
+
+}
+
+/*
+ * Switch overlay on or off
+ */
+
+static void zr36057_overlay(struct zoran *zr, int on)
+{
+	int fmt, bpp;
+	u32 reg;
+
+	if (on) {
+		/* do the necessary settings ... */
+
+		btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);	/* switch it off first */
+
+		switch (zr->buffer.depth) {
+		case 15:
+			fmt = VIDEO_PALETTE_RGB555;
+			bpp = 2;
+			break;
+		case 16:
+			fmt = VIDEO_PALETTE_RGB565;
+			bpp = 2;
+			break;
+		case 24:
+			fmt = VIDEO_PALETTE_RGB24;
+			bpp = 3;
+			break;
+		case 32:
+			fmt = VIDEO_PALETTE_RGB32;
+			bpp = 4;
+			break;
+		default:
+			fmt = 0;
+			bpp = 0;
+		}
+
+		zr36057_set_vfe(zr, zr->window.width, zr->window.height,
+				fmt);
+
+		/* Start and length of each line MUST be 4-byte aligned.
+		   This should be allready checked before the call to this routine.
+		   All error messages are internal driver checking only! */
+
+		/* video display top and bottom registers */
+
+		reg =
+		    (u32) zr->buffer.base + zr->window.x * bpp +
+		    zr->window.y * zr->buffer.bytesperline;
+		btwrite(reg, ZR36057_VDTR);
+		if (reg & 3)
+			printk(KERN_ERR
+			       "%s: zr36057_overlay: video_address not aligned\n",
+			       zr->name);
+		if (zr->video_interlace)
+			reg += zr->buffer.bytesperline;
+		btwrite(reg, ZR36057_VDBR);
+
+		/* video stride, status, and frame grab register */
+
+		reg = zr->buffer.bytesperline - zr->window.width * bpp;
+		if (zr->video_interlace)
+			reg += zr->buffer.bytesperline;
+		if (reg & 3)
+			printk(KERN_ERR
+			       "%s: zr36057_overlay: video_stride not aligned\n",
+			       zr->name);
+		reg = (reg << ZR36057_VSSFGR_DispStride);
+		reg |= ZR36057_VSSFGR_VidOvf;	/* clear overflow status */
+		btwrite(reg, ZR36057_VSSFGR);
+
+		/* Set overlay clipping */
+
+		if (zr->window.clipcount)
+			btor(ZR36057_OCR_OvlEnable, ZR36057_OCR);
+
+		/* ... and switch it on */
+
+		btor(ZR36057_VDCR_VidEn, ZR36057_VDCR);
+	} else {
+		/* Switch it off */
+
+		btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+	}
+}
+
+/*
+ * The overlay mask has one bit for each pixel on a scan line,
+ *  and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+static void write_overlay_mask(struct zoran *zr, struct video_clip *vp,
+			       int count)
+{
+	unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+	u32 *mask;
+	int x, y, width, height;
+	unsigned i, j, k;
+	u32 reg;
+
+	/* fill mask with one bits */
+	memset(zr->overlay_mask, ~0, mask_line_size * 4 * BUZ_MAX_HEIGHT);
+	reg = 0;
+
+	for (i = 0; i < count; ++i) {
+		/* pick up local copy of clip */
+		x = vp[i].x;
+		y = vp[i].y;
+		width = vp[i].width;
+		height = vp[i].height;
+
+		/* trim clips that extend beyond the window */
+		if (x < 0) {
+			width += x;
+			x = 0;
+		}
+		if (y < 0) {
+			height += y;
+			y = 0;
+		}
+		if (x + width > zr->window.width) {
+			width = zr->window.width - x;
+		}
+		if (y + height > zr->window.height) {
+			height = zr->window.height - y;
+		}
+
+		/* ignore degenerate clips */
+		if (height <= 0) {
+			continue;
+		}
+		if (width <= 0) {
+			continue;
+		}
+
+		/* apply clip for each scan line */
+		for (j = 0; j < height; ++j) {
+			/* reset bit for each pixel */
+			/* this can be optimized later if need be */
+			mask = zr->overlay_mask + (y + j) * mask_line_size;
+			for (k = 0; k < width; ++k) {
+				mask[(x + k) / 32] &=
+				    ~((u32) 1 << (x + k) % 32);
+			}
+		}
+	}
+}
+
+/* Enable/Disable uncompressed memory grabbing of the 36057 */
+
+static void zr36057_set_memgrab(struct zoran *zr, int mode)
+{
+	if (mode) {
+		if (btread(ZR36057_VSSFGR) &
+		    (ZR36057_VSSFGR_SnapShot | ZR36057_VSSFGR_FrameGrab))
+			printk(KERN_WARNING
+			       "%s: zr36057_set_memgrab_on with SnapShot or FrameGrab on ???\n",
+			       zr->name);
+
+		/* switch on VSync interrupts */
+
+		btwrite(IRQ_MASK, ZR36057_ISR);	// Clear Interrupts
+		btor(cardvsync[zr->card], ZR36057_ICR);	// SW
+
+		/* enable SnapShot */
+
+		btor(ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+
+		/* Set zr36057 video front end  and enable video */
+
+		zr36057_set_vfe(zr, zr->gwidth, zr->gheight, zr->gformat);
+
+		zr->v4l_memgrab_active = 1;
+	} else {
+		zr->v4l_memgrab_active = 0;
+
+		/* switch off VSync interrupts */
+
+		//btand(~ZR36057_ICR_GIRQ1, ZR36057_ICR); // SW
+
+		/* reenable grabbing to screen if it was running */
+
+		if (zr->v4l_overlay_active) {
+			zr36057_overlay(zr, 1);
+		} else {
+			btand(~ZR36057_VDCR_VidEn, ZR36057_VDCR);
+			btand(~ZR36057_VSSFGR_SnapShot, ZR36057_VSSFGR);
+		}
+	}
+}
+
+static int wait_grab_pending(struct zoran *zr)
+{
+	unsigned long flags;
+
+	/* wait until all pending grabs are finished */
+
+	if (!zr->v4l_memgrab_active)
+		return 0;
+
+	while (zr->v4l_pend_tail != zr->v4l_pend_head) {
+		interruptible_sleep_on(&zr->v4l_capq);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	spin_lock_irqsave(&zr->lock, flags);
+	zr36057_set_memgrab(zr, 0);
+	spin_unlock_irqrestore(&zr->lock, flags);
+
+	return 0;
+}
+
+/*
+ *   V4L Buffer grabbing
+ */
+
+static int v4l_grab(struct zoran *zr, struct video_mmap *mp)
+{
+	unsigned long flags;
+	int res, bpp;
+
+	/*
+	 * There is a long list of limitations to what is allowed to be grabbed
+	 * We don't output error messages here, since some programs (e.g. xawtv)
+	 * just try several settings to find out what is valid or not.
+	 */
+
+	/* No grabbing outside the buffer range! */
+
+	if (mp->frame >= v4l_nbufs || mp->frame < 0) {
+		DEBUG2(printk
+		       (KERN_ERR "%s: Can not grab frame %d\n", zr->name,
+			mp->frame));
+		return -EINVAL;
+	}
+
+	/* Check size and format of the grab wanted */
+
+	if (mp->height < BUZ_MIN_HEIGHT || mp->width < BUZ_MIN_WIDTH
+	    || mp->height > BUZ_MAX_HEIGHT || mp->width > BUZ_MAX_WIDTH) {
+		DEBUG2(printk
+		       (KERN_ERR "%s: Wrong frame size.\n", zr->name));
+		return -EINVAL;
+	}
+
+	bpp = format2bpp(mp->format);
+	if (bpp == 0) {
+		DEBUG2(printk
+		       (KERN_ERR "%s: Wrong bytes-per-pixel format\n",
+			zr->name));
+		return -EINVAL;
+	}
+
+	/* Check against available buffer size */
+
+	if (mp->height * mp->width * bpp > v4l_bufsize) {
+		DEBUG2(printk
+		       (KERN_ERR "%s: Video buffer size is too small\n",
+			zr->name));
+		return -EINVAL;
+	}
+
+	/* The video front end needs 4-byte alinged line sizes */
+
+	if ((bpp == 2 && (mp->width & 1)) || (bpp == 3 && (mp->width & 3))) {
+		DEBUG2(printk
+		       (KERN_ERR "%s: Wrong frame alingment\n", zr->name));
+		return -EINVAL;
+	}
+
+	/*
+	 * To minimize the time spent in the IRQ routine, we avoid setting up
+	 * the video front end there.
+	 * If this grab has different parameters from a running streaming capture
+	 * we stop the streaming capture and start it over again.
+	 */
+
+	if (zr->v4l_memgrab_active
+	    && (zr->gwidth != mp->width || zr->gheight != mp->height
+		|| zr->gformat != mp->format)) {
+		res = wait_grab_pending(zr);
+		if (res)
+			return res;
+	}
+	zr->gwidth = mp->width;
+	zr->gheight = mp->height;
+	zr->gformat = mp->format;
+	zr->gbpl = bpp * zr->gwidth;
+
+
+	spin_lock_irqsave(&zr->lock, flags);
+
+	/* make sure a grab isn't going on currently with this buffer */
+
+	switch (zr->v4l_gbuf[mp->frame].state) {
+
+	default:
+	case BUZ_STATE_PEND:
+		res = -EBUSY;	/* what are you doing? */
+		break;
+
+	case BUZ_STATE_USER:
+	case BUZ_STATE_DONE:
+		/* since there is at least one unused buffer there's room for at least one more pend[] entry */
+		zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] =
+		    mp->frame;
+		zr->v4l_gbuf[mp->frame].state = BUZ_STATE_PEND;
+		res = 0;
+		break;
+
+	}
+
+	/* put the 36057 into frame grabbing mode */
+
+	if (!res && !zr->v4l_memgrab_active)
+		zr36057_set_memgrab(zr, 1);
+
+	spin_unlock_irqrestore(&zr->lock, flags);
+	//DEBUG2(printk(KERN_INFO "%s: Frame grab 3...\n", zr->name));
+
+	return res;
+}
+
+/*
+ * Sync on a V4L buffer
+ */
+
+static int v4l_sync(struct zoran *zr, int frame)
+{
+	unsigned long flags;
+
+	/* check passed-in frame number */
+
+	if (frame >= v4l_nbufs || frame < 0) {
+		DEBUG1(printk(KERN_ERR "%s: v4l_sync: frame %d is invalid\n",
+		       zr->name, frame));
+		return -EINVAL;
+	}
+
+	/* Check if is buffer was queued at all */
+
+	if (zr->v4l_gbuf[frame].state == BUZ_STATE_USER) {
+		DEBUG1(printk(KERN_ERR
+		       "%s: v4l_sync: Attempt to sync on a buffer which was not queued?\n",
+		       zr->name));
+		return -EPROTO;
+	}
+
+	/* wait on this buffer to get ready */
+
+	while (zr->v4l_gbuf[frame].state == BUZ_STATE_PEND) {
+		interruptible_sleep_on(&zr->v4l_capq);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	/* buffer should now be in BUZ_STATE_DONE */
+
+	if (zr->v4l_gbuf[frame].state != BUZ_STATE_DONE)
+		printk(KERN_ERR "%s: v4l_sync - internal error\n",
+		       zr->name);
+
+	/* Check if streaming capture has finished */
+
+	spin_lock_irqsave(&zr->lock, flags);
+
+	if (zr->v4l_pend_tail == zr->v4l_pend_head)
+		zr36057_set_memgrab(zr, 0);
+
+	spin_unlock_irqrestore(&zr->lock, flags);
+
+	return 0;
+}
+
+/*****************************************************************************
+ *                                                                           *
+ *  Set up the Buz-specific MJPEG part                                       *
+ *                                                                           *
+ *****************************************************************************/
+
+/*
+Wait til post office is no longer busy */
+static int post_office_wait(struct zoran *zr)
+{
+	u32 por;
+
+//      while (((por = btread(ZR36057_POR)) & (ZR36057_POR_POPen | ZR36057_POR_POTime)) == ZR36057_POR_POPen) {
+	while ((por = btread(ZR36057_POR)) & ZR36057_POR_POPen) {
+		/* wait for something to happen */
+	}
+	if ((por & ZR36057_POR_POTime) && zr->card != LML33
+	    && zr->card != BUZ) {
+		/* In LML33/BUZ \GWS line is not connected, so it has always timeout set */
+		printk(KERN_WARNING "%s: pop timeout %08x\n", zr->name, por);
+		return -1;
+	}
+	return 0;
+}
+
+static int post_office_write(struct zoran *zr, unsigned guest,
+			     unsigned reg, unsigned value)
+{
+	u32 por;
+
+	por =
+	    ZR36057_POR_PODir | ZR36057_POR_POTime | ((guest & 7) << 20) |
+	    ((reg & 7) << 16) | (value & 0xFF);
+	btwrite(por, ZR36057_POR);
+	return post_office_wait(zr);
+}
+
+static int post_office_read(struct zoran *zr, unsigned guest, unsigned reg)
+{
+	u32 por;
+
+	por = ZR36057_POR_POTime | ((guest & 7) << 20) | ((reg & 7) << 16);
+	btwrite(por, ZR36057_POR);
+	if (post_office_wait(zr) < 0) {
+		return -1;
+	}
+	return btread(ZR36057_POR) & 0xFF;
+}
+
+static int zr36060_write_8(struct zoran *zr, unsigned reg, unsigned val)
+{
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 0, 1, reg >> 8)
+	    || post_office_write(zr, 0, 2, reg)) {
+		return -1;
+	}
+	return post_office_write(zr, 0, 3, val);
+}
+
+static int zr36060_write_16(struct zoran *zr, unsigned reg, unsigned val)
+{
+	if (zr36060_write_8(zr, reg + 0, val >> 8)) {
+		return -1;
+	}
+	return zr36060_write_8(zr, reg + 1, val >> 0);
+}
+
+static int zr36060_write_24(struct zoran *zr, unsigned reg, unsigned val)
+{
+	if (zr36060_write_8(zr, reg + 0, val >> 16)) {
+		return -1;
+	}
+	return zr36060_write_16(zr, reg + 1, val >> 0);
+}
+
+static int zr36060_write_32(struct zoran *zr, unsigned reg, unsigned val)
+{
+	if (zr36060_write_16(zr, reg + 0, val >> 16)) {
+		return -1;
+	}
+	return zr36060_write_16(zr, reg + 2, val >> 0);
+}
+
+static u32 zr36060_read_8(struct zoran *zr, unsigned reg)
+{
+	if (post_office_wait(zr)
+	    || post_office_write(zr, 0, 1, reg >> 8)
+	    || post_office_write(zr, 0, 2, reg)) {
+		return -1;
+	}
+	return post_office_read(zr, 0, 3) & 0xFF;
+}
+
+/* ----------------------------------------------------------------------- */
+
+static void GPIO(struct zoran *zr, unsigned bit, unsigned value)
+{
+	u32 reg;
+	u32 mask;
+
+	mask = 1 << (24 + bit);
+	reg = btread(ZR36057_GPPGCR1) & ~mask;
+	if (value) {
+		reg |= mask;
+	}
+	btwrite(reg, ZR36057_GPPGCR1);
+	udelay(1);
+}
+
+
+static void zr36060_sleep(struct zoran *zr, int sleep)
+{
+	switch (zr->card) {
+	case DC10:
+	case DC10plus:
+		GPIO(zr, 3, !sleep);
+		break;
+	case BUZ:
+	case LML33:
+		GPIO(zr, 1, !sleep);
+		break;
+	default:
+		break;
+	}
+	if (!sleep)
+		udelay(500);
+	else
+		udelay(2);
+}
+
+static int zr36060_reset(struct zoran *zr)
+{
+	switch (zr->card) {
+	case DC10:
+	case DC10plus:
+		zr36060_sleep(zr, 0);
+		GPIO(zr, 0, 0);
+		udelay(2);
+		GPIO(zr, 0, 1);
+		udelay(2);
+		break;
+	case LML33:
+	case BUZ:
+		zr36060_sleep(zr, 0);
+		post_office_write(zr, 3, 0, 0);
+		udelay(2);
+	default:
+	}
+	return 0;
+}
+
+static void set_frame(struct zoran *zr, int val)
+{
+	switch (zr->card) {
+	case DC10:
+	case DC10plus:
+		GPIO(zr, 6, val);
+		break;
+	case LML33:
+	case BUZ:
+		GPIO(zr, 3, val);
+		break;
+	default:
+		break;
+	}
+}
+
+static void set_videobus_dir(struct zoran *zr, int val)
+{
+	switch (zr->card) {
+	case DC10:
+	case DC10plus:
+		GPIO(zr, 1, val);
+		break;
+	case LML33:
+		if (lml33dpath == 0)
+			GPIO(zr, 5, val);
+		else
+			GPIO(zr, 5, 1);
+		break;
+	case BUZ:
+	default:
+		break;
+	}
+}
+
+static void set_videobus_enable(struct zoran *zr, int val)
+{
+	switch (zr->card) {
+	case LML33:
+		GPIO(zr, 7, val);
+		break;
+	case DC10:
+	case DC10plus:
+	case BUZ:
+	default:
+		break;
+	}
+}
+
+static void zr36060_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
+{
+	struct tvnorm *tvn;
+	u32 reg;
+	int size, blocks;
+
+	reg = (1 << 0)		/* CodeMstr */
+	    |(0 << 2)		/* CFIS=0 */
+	    |(0 << 6)		/* Endian=0 */
+	    |(0 << 7);		/* Code16=0 */
+	zr36060_write_8(zr, 0x002, reg);
+
+	switch (mode) {
+
+	case BUZ_MODE_MOTION_DECOMPRESS:
+	case BUZ_MODE_STILL_DECOMPRESS:
+		reg = 0x00;	/* Codec mode = decompression */
+		break;
+
+	case BUZ_MODE_MOTION_COMPRESS:
+	case BUZ_MODE_STILL_COMPRESS:
+	default:
+		reg = 0xa4;	/* Codec mode = compression with variable scale factor */
+		break;
+
+	}
+	zr36060_write_8(zr, 0x003, reg);
+
+	reg = 0x00;		/* reserved, mbz */
+	zr36060_write_8(zr, 0x004, reg);
+
+	/* code volume */
+
+	/* Target field size in pixels: */
+	tvn = zr->timing;
+	size =
+	    (tvn->Ha / 2) * (tvn->Wa) / (zr->params.HorDcm) /
+	    (zr->params.VerDcm);
+	blocks = size / 64;
+
+	/* Target compressed field size in bits: */
+	size = size * 16;	/* uncompressed size in bits */
+	size = size * zr->params.quality / 400;	/* quality = 100 is a compression ratio 1:4 */
+
+	/* Lower limit (arbitrary, 1 KB) */
+	if (size < 8192)
+		size = 8192;
+
+	/* Upper limit: 6/8 of the code buffers */
+	if (size * zr->params.field_per_buff > zr->jpg_bufsize * 6)
+		size = zr->jpg_bufsize * 6 / zr->params.field_per_buff;
+
+	reg = size * 4 / blocks;
+	if (reg > 0xf0)
+		reg = 0xf0;	/* 480 bits/block, does 0xff represents unlimited? */
+	zr36060_write_8(zr, 0x005, reg);
+
+	/* JPEG markers */
+	reg = (zr->params.jpeg_markers) & 0x38;	/* DRI, DQT, DHT */
+	if (zr->params.COM_len)
+		reg |= JPEG_MARKER_COM;
+	if (zr->params.APP_len)
+		reg |= JPEG_MARKER_APP;
+	zr36060_write_8(zr, 0x006, reg);
+
+	if (zr->card != LML33 && zr->card != BUZ) {
+		reg = (0 << 3)	/* EOAV=0 */
+		    |(0 << 2)	/* EOI=0 */
+		    |(0 << 1)	/* END=0 */
+		    |(1 << 0);	/* DATERR=1 */
+	} else {
+		reg = (0 << 3)	/* EOAV=0 */
+		    |(0 << 2)	/* EOI=0 */
+		    |(0 << 1)	/* END=0 */
+		    |(0 << 0);	/* DATERR=0 */
+	}
+	zr36060_write_8(zr, 0x007, reg);
+
+	reg = size;
+	zr36060_write_32(zr, 0x009, reg);
+
+	reg = (size * 10) / 11;
+	zr36060_write_32(zr, 0x00d, reg);	// Not needed for compr. with variable scale factor, just in case ...
+
+	/* how do we set initial SF as a function of quality parameter? */
+	reg = 0x0100;		/* SF=1.0 */
+	zr36060_write_16(zr, 0x011, reg);
+
+	reg = 0x00ffffff;	/* AF=max */
+	zr36060_write_24(zr, 0x013, reg);
+
+	reg = 0x0000;		/* test */
+	zr36060_write_16(zr, 0x024, reg);
+
+	//post_office_read(zr,1,0);
+}
+
+static void zr36060_set_video(struct zoran *zr, enum zoran_codec_mode mode)
+{
+	struct tvnorm *tvn;
+	u32 reg;
+	unsigned HStart;
+
+	tvn = zr->timing;
+
+	reg = (0 << 7)		/* Video8     */
+	    |(0 << 6)		/* Range      */
+	    |(0 << 3)		/* FlDet      */
+	    |(1 << 2)		/* FlVedge    */
+	    |(0 << 1)		/* FlExt      */
+	    |(0 << 0);		/* SyncMstr   */
+
+	if (mode != BUZ_MODE_STILL_DECOMPRESS) {
+		/* limit pixels to range 16..235 as per CCIR-601 */
+		reg |= (1 << 6);	/* Range=1 */
+	}
+	zr36060_write_8(zr, 0x030, reg);
+
+	switch (zr->card) {
+	case DC10:
+	case DC10plus:
+		reg = (0 << 7)	/* VCLKPol   */
+		    |(0 << 6)	/* PValPol   */
+		    |(0 << 5)	/* PoePol    */
+		    |(0 << 4)	/* SImgPol   */
+		    |(1 << 3)	/* BLPol     */
+		    |(1 << 2)	/* FlPol     */
+		    |(1 << 1)	/* HSPol     */
+		    |(1 << 0);	/* VSPol     */
+		break;
+	case LML33:
+		reg = (0 << 7)	/* VCLKPol=0 */
+		    |(0 << 6)	/* PValPol=0 */
+		    |(1 << 5)	/* PoePol=1 */
+		    |(0 << 4)	/* SImgPol=0 */
+		    |(0 << 3)	/* BLPol=0 */
+		    |(0 << 2)	/* FlPol=0 */
+		    |(0 << 1)	/* HSPol=0, sync on falling edge */
+		    |(1 << 0);	/* VSPol=1 */
+		break;
+	case BUZ:
+	default:
+		reg = (0 << 7)	/* VCLKPol=0 */
+		    |(0 << 6)	/* PValPol=0 */
+		    |(1 << 5)	/* PoePol=1 */
+		    |(0 << 4)	/* SImgPol=0 */
+		    |(0 << 3)	/* BLPol=0 */
+		    |(0 << 2)	/* FlPol=0 */
+		    |(1 << 1)	/* HSPol=0, sync on falling edge */
+		    |(1 << 0);	/* VSPol=1 */
+		break;
+	}
+	zr36060_write_8(zr, 0x031, reg);
+
+	switch (zr->params.HorDcm) {
+	default:
+	case 1:
+		reg = (0 << 0);
+		break;		/* HScale = 0 */
+
+	case 2:
+		reg = (1 << 0);
+		break;		/* HScale = 1 */
+
+	case 4:
+		reg = (2 << 0);
+		break;		/* HScale = 2 */
+	}
+	if (zr->params.VerDcm == 2)
+		reg |= (1 << 2);
+	zr36060_write_8(zr, 0x032, reg);
+
+	reg = 0x00;		/* BackY */
+	zr36060_write_8(zr, 0x033, reg);
+
+	reg = 0x80;		/* BackU */
+	zr36060_write_8(zr, 0x034, reg);
+
+	reg = 0x80;		/* BackV */
+	zr36060_write_8(zr, 0x035, reg);
+
+	/* sync generator */
+
+	reg = tvn->Ht - 1;	/* Vtotal */
+	zr36060_write_16(zr, 0x036, reg);
+
+	reg = tvn->Wt - 1;	/* Htotal */
+	zr36060_write_16(zr, 0x038, reg);
+
+	reg = 6 - 1;		/* VsyncSize */
+	zr36060_write_8(zr, 0x03a, reg);
+
+	//reg   = 30 - 1;               /* HsyncSize */
+	reg = (zr->params.norm == 1 ? 57 : 68);
+	zr36060_write_8(zr, 0x03b, reg);
+
+	reg = tvn->VStart - 1;	/* BVstart */
+	zr36060_write_8(zr, 0x03c, reg);
+
+	reg += tvn->Ha / 2;	/* BVend */
+	zr36060_write_16(zr, 0x03e, reg);
+
+	reg = tvn->HStart + 64 - 1;	/* BHstart */
+	zr36060_write_8(zr, 0x03d, reg);
+
+	reg += tvn->Wa;		/* BHend */
+	zr36060_write_16(zr, 0x040, reg);
+
+	/* active area */
+	reg = zr->params.img_y + tvn->VStart;	/* Vstart */
+	zr36060_write_16(zr, 0x042, reg);
+
+	reg += zr->params.img_height;	/* Vend */
+	zr36060_write_16(zr, 0x044, reg);
+
+	HStart = tvn->HStart;
+	if (zr->card == BUZ) {
+		HStart += 44;
+	} else {
+		HStart += 64;
+	}
+	reg = zr->params.img_x + HStart;	/* Hstart */
+	zr36060_write_16(zr, 0x046, reg);
+
+	reg += zr->params.img_width;	/* Hend */
+	zr36060_write_16(zr, 0x048, reg);
+
+	/* subimage area */
+	reg = tvn->VStart - 4;	/* SVstart */
+	zr36060_write_16(zr, 0x04a, reg);
+
+	reg += tvn->Ha / 2 + 8;	/* SVend */
+	zr36060_write_16(zr, 0x04c, reg);
+
+	reg = tvn->HStart + 64 - 4;	/* SHstart */
+	zr36060_write_16(zr, 0x04e, reg);
+
+	reg += tvn->Wa + 8;	/* SHend */
+	zr36060_write_16(zr, 0x050, reg);
+}
+
+static void zr36060_set_jpg_SOF(struct zoran *zr)
+{
+	u32 reg;
+
+
+	reg = 0xffc0;		/* SOF marker */
+	zr36060_write_16(zr, 0x060, reg);
+
+	reg = 17;		/* SOF length */
+	zr36060_write_16(zr, 0x062, reg);
+
+	reg = 8;		/* precision 8 bits */
+	zr36060_write_8(zr, 0x064, reg);
+
+	reg = zr->params.img_height / zr->params.VerDcm;	/* image height */
+	zr36060_write_16(zr, 0x065, reg);
+
+	reg = zr->params.img_width / zr->params.HorDcm;	/* image width */
+	zr36060_write_16(zr, 0x067, reg);
+
+	reg = 3;		/* 3 color components */
+	zr36060_write_8(zr, 0x069, reg);
+
+	reg = 0x002100;		/* Y component */
+	zr36060_write_24(zr, 0x06a, reg);
+
+	reg = 0x011101;		/* U component */
+	zr36060_write_24(zr, 0x06d, reg);
+
+	reg = 0x021101;		/* V component */
+	zr36060_write_24(zr, 0x070, reg);
+}
+
+static void zr36060_set_jpg_SOS(struct zoran *zr)
+{
+	u32 reg;
+
+
+	reg = 0xffda;		/* SOS marker */
+	zr36060_write_16(zr, 0x07a, reg);
+
+	reg = 12;		/* SOS length */
+	zr36060_write_16(zr, 0x07c, reg);
+
+	reg = 3;		/* 3 color components */
+	zr36060_write_8(zr, 0x07e, reg);
+
+	reg = 0x0000;		/* Y component */
+	zr36060_write_16(zr, 0x07f, reg);
+
+	reg = 0x0111;		/* U component */
+	zr36060_write_16(zr, 0x081, reg);
+
+	reg = 0x0211;		/* V component */
+	zr36060_write_16(zr, 0x083, reg);
+
+	reg = 0x003f00;		/* Start, end spectral scans */
+	zr36060_write_24(zr, 0x085, reg);
+}
+
+static void zr36060_set_jpg_DRI(struct zoran *zr)
+{
+	u32 reg;
+
+
+	reg = 0xffdd;		/* DRI marker */
+	zr36060_write_16(zr, 0x0c0, reg);
+
+	reg = 4;		/* DRI length */
+	zr36060_write_16(zr, 0x0c2, reg);
+
+	reg = 8;		/* length in MCUs */
+	zr36060_write_16(zr, 0x0c4, reg);
+}
+
+static void zr36060_set_jpg_DQT(struct zoran *zr)
+{
+	unsigned i;
+	unsigned adr;
+	static const u8 dqt[] = {
+		0xff, 0xdb,	/* DHT marker */
+		0x00, 0x84,	/* DHT length */
+		0x00,		/* table ID 0 */
+		0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
+		0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
+		0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
+		0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
+		0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
+		0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
+		0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
+		0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+		0x01,		/* table ID 1 */
+		0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
+		0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+		0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63
+	};
+
+	/* write fixed quantitization tables */
+	adr = 0x0cc;
+	for (i = 0; i < sizeof(dqt); ++i) {
+		zr36060_write_8(zr, adr++, dqt[i]);
+	}
+}
+
+static void zr36060_set_jpg_DHT(struct zoran *zr)
+{
+	unsigned i;
+	unsigned adr;
+	static const u8 dht[] = {
+		0xff, 0xc4,	/* DHT marker */
+		0x01, 0xa2,	/* DHT length */
+		0x00,		/* table class 0, ID 0 */
+		0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01,	/* # codes of length 1..8 */
+		0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* # codes of length 8..16 */
+		0x00,		/* values for codes of length 2 */
+		0x01, 0x02, 0x03, 0x04, 0x05,	/* values for codes of length 3 */
+		0x06,		/* values for codes of length 4 */
+		0x07,		/* values for codes of length 5 */
+		0x08,		/* values for codes of length 6 */
+		0x09,		/* values for codes of length 7 */
+		0x0a,		/* values for codes of length 8 */
+		0x0b,		/* values for codes of length 9 */
+		0x01,		/* table class 0, ID 1 */
+		0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,	/* # codes of length 1..8 */
+		0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,	/* # codes of length 9..16 */
+		0x00, 0x01, 0x02,	/* values for codes of length 2 */
+		0x03,		/* values for codes of length 3 */
+		0x04,		/* values for codes of length 4 */
+		0x05,		/* values for codes of length 5 */
+		0x06,		/* values for codes of length 6 */
+		0x07,		/* values for codes of length 7 */
+		0x08,		/* values for codes of length 8 */
+		0x09,		/* values for codes of length 9 */
+		0x0a,		/* values for codes of length 10 */
+		0x0b,		/* values for codes of length 11 */
+		0x10,
+		0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03,
+		0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d,
+		0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12,
+		0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
+		0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08,
+		0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0,
+		0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16,
+		0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28,
+		0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+		0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
+		0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59,
+		0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+		0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
+		0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
+		0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98,
+		0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+		0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6,
+		0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5,
+		0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4,
+		0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2,
+		0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea,
+		0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8,
+		0xf9, 0xfa, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04,
+		0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00,
+		0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11,
+		0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51,
+		0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08,
+		0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23,
+		0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a,
+		0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18,
+		0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35,
+		0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45,
+		0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55,
+		0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65,
+		0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75,
+		0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84,
+		0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93,
+		0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2,
+		0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa,
+		0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+		0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8,
+		0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+		0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6,
+		0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5,
+		0xf6, 0xf7, 0xf8, 0xf9, 0xfa
+	};
+
+	/* write fixed Huffman tables */
+	adr = 0x1d4;
+	for (i = 0; i < sizeof(dht); ++i) {
+		zr36060_write_8(zr, adr++, dht[i]);
+	}
+}
+
+static void zr36060_set_jpg_APP(struct zoran *zr)
+{
+	unsigned adr;
+	int len, i;
+	u32 reg;
+
+
+	len = zr->params.APP_len;
+	if (len < 0)
+		len = 0;
+	if (len > 60)
+		len = 60;
+
+	i = zr->params.APPn;
+	if (i < 0)
+		i = 0;
+	if (i > 15)
+		i = 15;
+
+	reg = 0xffe0 + i;	/* APPn marker */
+	zr36060_write_16(zr, 0x380, reg);
+
+	reg = len + 2;		/* APPn len */
+	zr36060_write_16(zr, 0x382, reg);
+
+	/* write APPn data */
+	adr = 0x384;
+	for (i = 0; i < 60; i++) {
+		zr36060_write_8(zr, adr++,
+				(i < len ? zr->params.APP_data[i] : 0));
+	}
+}
+
+static void zr36060_set_jpg_COM(struct zoran *zr)
+{
+	unsigned adr;
+	int len, i;
+	u32 reg;
+
+
+	len = zr->params.COM_len;
+	if (len < 0)
+		len = 0;
+	if (len > 60)
+		len = 60;
+
+	reg = 0xfffe;		/* COM marker */
+	zr36060_write_16(zr, 0x3c0, reg);
+
+	reg = len + 2;		/* COM len */
+	zr36060_write_16(zr, 0x3c2, reg);
+
+	/* write COM data */
+	adr = 0x3c4;
+	for (i = 0; i < 60; i++) {
+		zr36060_write_8(zr, adr++,
+				(i < len ? zr->params.COM_data[i] : 0));
+	}
+}
+
+static void zr36060_set_cap(struct zoran *zr, enum zoran_codec_mode mode)
+{
+	unsigned i;
+	u32 reg;
+
+	zr36060_reset(zr);
+	mdelay(10);
+
+	reg = (0 << 7)		/* Load=0 */
+	    |(1 << 0);		/* SynRst=1 */
+	zr36060_write_8(zr, 0x000, reg);
+
+	zr36060_set_jpg(zr, mode);
+	zr36060_set_video(zr, mode);
+	zr36060_set_jpg_SOF(zr);
+	zr36060_set_jpg_SOS(zr);
+	zr36060_set_jpg_DRI(zr);
+	zr36060_set_jpg_DQT(zr);
+	zr36060_set_jpg_DHT(zr);
+	zr36060_set_jpg_APP(zr);
+	zr36060_set_jpg_COM(zr);
+
+	reg = (1 << 7)		/* Load=1 */
+	    |(1 << 0);		/* SynRst=0 */
+	zr36060_write_8(zr, 0x000, reg);
+
+	/* wait for codec to unbusy */
+	for (i = 0; i < 100000; ++i) {
+		reg = zr36060_read_8(zr, 0x001);
+		if ((reg & (1 << 7)) == 0) {
+			return;
+		}
+		//udelay(100);
+	}
+	printk(KERN_ERR "%sZR36060: stuck busy, statux=%02x\n", zr->name,
+	       reg);
+}
+
+static void init_jpeg_queue(struct zoran *zr)
+{
+	int i;
+	/* re-initialize DMA ring stuff */
+	zr->jpg_que_head = 0;
+	zr->jpg_dma_head = 0;
+	zr->jpg_dma_tail = 0;
+	zr->jpg_que_tail = 0;
+	zr->jpg_seq_num = 0;
+	zr->JPEG_error = 0;
+	zr->num_errors = 0;
+	zr->jpg_err_seq = 0;
+	zr->jpg_err_shift = 0;
+	zr->jpg_queued_num = 0;
+	for (i = 0; i < zr->jpg_nbufs; i++) {
+		zr->jpg_gbuf[i].state = BUZ_STATE_USER;	/* nothing going on */
+	}
+	for (i = 0; i < BUZ_NUM_STAT_COM; i++) {
+		zr->stat_com[i] = 1;	/* mark as unavailable to zr36057 */
+	}
+}
+
+static void zr36057_set_jpg(struct zoran *zr, enum zoran_codec_mode mode)
+{
+	struct tvnorm *tvn;
+	u32 reg;
+
+	tvn = zr->timing;
+
+	/* assert P_Reset */
+	btwrite(0, ZR36057_JPC);
+
+	/* MJPEG compression mode */
+	switch (mode) {
+
+	case BUZ_MODE_MOTION_COMPRESS:
+	default:
+		reg = ZR36057_JMC_MJPGCmpMode;
+		break;
+
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		reg = ZR36057_JMC_MJPGExpMode;
+		reg |= ZR36057_JMC_SyncMstr;
+		/* RJ: The following is experimental - improves the output to screen */
+		//if(zr->params.VFIFO_FB) reg |= ZR36057_JMC_VFIFO_FB; // No, it doesn't. SM
+		break;
+
+	case BUZ_MODE_STILL_COMPRESS:
+		reg = ZR36057_JMC_JPGCmpMode;
+		break;
+
+	case BUZ_MODE_STILL_DECOMPRESS:
+		reg = ZR36057_JMC_JPGExpMode;
+		break;
+
+	}
+	reg |= ZR36057_JMC_JPG;
+	if (zr->params.field_per_buff == 1)
+		reg |= ZR36057_JMC_Fld_per_buff;
+	btwrite(reg, ZR36057_JMC);
+
+	/* vertical */
+	btor(ZR36057_VFEVCR_VSPol, ZR36057_VFEVCR);
+	reg =
+	    (6 << ZR36057_VSP_VsyncSize) | (tvn->Ht << ZR36057_VSP_FrmTot);
+	btwrite(reg, ZR36057_VSP);
+	reg = ((zr->params.img_y + tvn->VStart) << ZR36057_FVAP_NAY)
+	    | (zr->params.img_height << ZR36057_FVAP_PAY);
+	btwrite(reg, ZR36057_FVAP);
+
+	/* horizontal */
+	btor(ZR36057_VFEHCR_HSPol, ZR36057_VFEHCR);
+	reg =
+	    ((tvn->HSyncStart) << ZR36057_HSP_HsyncStart) | (tvn->
+							     Wt <<
+							     ZR36057_HSP_LineTot);
+	btwrite(reg, ZR36057_HSP);
+	reg = ((zr->params.img_x + tvn->HStart + 4) << ZR36057_FHAP_NAX)
+	    | (zr->params.img_width << ZR36057_FHAP_PAX);
+	btwrite(reg, ZR36057_FHAP);
+
+	/* field process parameters */
+	if (zr->params.odd_even)
+		reg = ZR36057_FPP_Odd_Even;
+	else
+		reg = 0;
+	if (mode == BUZ_MODE_MOTION_DECOMPRESS && zr->card != LML33
+	    && zr->card != BUZ)
+		reg ^= ZR36057_FPP_Odd_Even;
+
+	btwrite(reg, ZR36057_FPP);
+
+	/* Set proper VCLK Polarity, else colors will be wrong during playback */
+	//btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
+
+	/* code base address */
+	reg = virt_to_bus(zr->stat_com);
+	btwrite(reg, ZR36057_JCBA);
+
+	/* FIFO threshold (FIFO is 160. double words) */
+	/* NOTE: decimal values here */
+	switch (mode) {
+
+	case BUZ_MODE_STILL_COMPRESS:
+	case BUZ_MODE_MOTION_COMPRESS:
+		reg = 140;
+		break;
+
+	case BUZ_MODE_STILL_DECOMPRESS:
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		reg = 20;
+		break;
+
+	default:
+		reg = 80;
+		break;
+
+	}
+	btwrite(reg, ZR36057_JCFT);
+	zr36057_adjust_vfe(zr, mode);
+
+}
+
+#if (DEBUGLEVEL > 2)
+static void dump_guests(struct zoran *zr)
+{
+	int i, guest[8];
+
+	for (i = 1; i < 8; i++) {	// Don't read zr36060 here
+		guest[i] = post_office_read(zr, i, 0);
+	}
+
+	printk(KERN_INFO "%s: Guests:", zr->name);
+
+	for (i = 1; i < 8; i++) {
+		printk(" 0x%02x", guest[i]);
+	}
+	printk("\n");
+}
+
+static unsigned long get_time(void)
+{
+	struct timeval tv;
+	do_gettimeofday(&tv);
+	return (1000000 * tv.tv_sec + tv.tv_usec);
+}
+
+static void detect_guest_activity(struct zoran *zr)
+{
+	int timeout, i, j, res, guest[8], guest0[8], change[8][3];
+	unsigned long t0, t1;
+
+	dump_guests(zr);
+	printk(KERN_INFO "%s: Detecting guests activity, please wait...\n",
+	       zr->name);
+	for (i = 1; i < 8; i++) {	// Don't read zr36060 here
+		guest0[i] = guest[i] = post_office_read(zr, i, 0);
+	}
+
+	timeout = 0;
+	j = 0;
+	t0 = get_time();
+	while (timeout < 10000) {
+		udelay(10);
+		timeout++;
+		for (i = 1; (i < 8) && (j < 8); i++) {
+			res = post_office_read(zr, i, 0);
+			if (res != guest[i]) {
+				t1 = get_time();
+				change[j][0] = (t1 - t0);
+				t0 = t1;
+				change[j][1] = i;
+				change[j][2] = res;
+				j++;
+				guest[i] = res;
+			}
+		}
+		if (j >= 8)
+			break;
+	}
+	printk(KERN_INFO "%s: Guests:", zr->name);
+
+	for (i = 1; i < 8; i++) {
+		printk(" 0x%02x", guest0[i]);
+	}
+	printk("\n");
+	if (j == 0) {
+		printk(KERN_INFO "%s: No activity detected.\n", zr->name);
+		return;
+	}
+	for (i = 0; i < j; i++) {
+		printk(KERN_INFO "%s: %6d: %d => 0x%02x\n", zr->name,
+		       change[i][0], change[i][1], change[i][2]);
+	}
+}
+#endif
+
+static void print_interrupts(struct zoran *zr)
+{
+	int res, noerr;
+	noerr = 0;
+	printk(KERN_INFO "%s: interrupts received:", zr->name);
+	if ((res = zr->field_counter) < -1 || res > 1) {
+		printk(" FD:%d", res);
+	}
+	if ((res = zr->intr_counter_GIRQ1) != 0) {
+		printk(" GIRQ1:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_GIRQ0) != 0) {
+		printk(" GIRQ0:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_CodRepIRQ) != 0) {
+		printk(" CodRepIRQ:%d", res);
+		noerr++;
+	}
+	if ((res = zr->intr_counter_JPEGRepIRQ) != 0) {
+		printk(" JPEGRepIRQ:%d", res);
+		noerr++;
+	}
+	if (zr->JPEG_max_missed) {
+		printk(" JPEG delays: max=%d min=%d", zr->JPEG_max_missed,
+		       zr->JPEG_min_missed);
+	}
+	if (zr->END_event_missed) {
+		printk(" ENDs missed: %d", zr->END_event_missed);
+	}
+	//if (zr->jpg_queued_num) {
+	printk(" queue_state=%ld/%ld/%ld/%ld", zr->jpg_que_tail,
+	       zr->jpg_dma_tail, zr->jpg_dma_head, zr->jpg_que_head);
+	//}
+	if (!noerr) {
+		printk(": no interrupts detected.");
+	}
+	printk("\n");
+}
+
+static void clear_interrupt_counters(struct zoran *zr)
+{
+	zr->intr_counter_GIRQ1 = 0;
+	zr->intr_counter_GIRQ0 = 0;
+	zr->intr_counter_CodRepIRQ = 0;
+	zr->intr_counter_JPEGRepIRQ = 0;
+	zr->field_counter = 0;
+	zr->IRQ1_in = 0;
+	zr->IRQ1_out = 0;
+	zr->JPEG_in = 0;
+	zr->JPEG_out = 0;
+	zr->JPEG_0 = 0;
+	zr->JPEG_1 = 0;
+	zr->END_event_missed = 0;
+	zr->JPEG_missed = 0;
+	zr->JPEG_max_missed = 0;
+	zr->JPEG_min_missed = 0x7fffffff;
+}
+
+static u32 count_reset_interrupt(struct zoran *zr)
+{
+	u32 isr;
+	if ((isr = btread(ZR36057_ISR) & 0x78000000)) {
+		if (isr & ZR36057_ISR_GIRQ1) {
+			btwrite(ZR36057_ISR_GIRQ1, ZR36057_ISR);
+			zr->intr_counter_GIRQ1++;
+		}
+		if (isr & ZR36057_ISR_GIRQ0) {
+			btwrite(ZR36057_ISR_GIRQ0, ZR36057_ISR);
+			zr->intr_counter_GIRQ0++;
+		}
+		if (isr & ZR36057_ISR_CodRepIRQ) {
+			btwrite(ZR36057_ISR_CodRepIRQ, ZR36057_ISR);
+			zr->intr_counter_CodRepIRQ++;
+		}
+		if (isr & ZR36057_ISR_JPEGRepIRQ) {
+			btwrite(ZR36057_ISR_JPEGRepIRQ, ZR36057_ISR);
+			zr->intr_counter_JPEGRepIRQ++;
+		}
+	}
+	return isr;
+}
+
+static void jpeg_start(struct zoran *zr)
+{
+	int reg;
+	zr->frame_num = 0;
+
+	btwrite(ZR36057_JPC_P_Reset, ZR36057_JPC);	// /P_Reset
+	btand(~ZR36057_MCTCR_CFlush, ZR36057_MCTCR);	// \CFlush
+	btor(ZR36057_JPC_CodTrnsEn, ZR36057_JPC);	// /CodTrnsEn
+	btwrite(IRQ_MASK, ZR36057_ISR);	// Clear IRQs
+	btwrite(IRQ_MASK | ZR36057_ICR_IntPinEn, ZR36057_ICR);	// Enable IRQs
+
+	set_frame(zr, 0);	// \FRAME
+
+	/* JPEG codec guest ID */
+	reg =
+	    (1 << ZR36057_JCGI_JPEGuestID) | (0 <<
+					      ZR36057_JCGI_JPEGuestReg);
+	btwrite(reg, ZR36057_JCGI);
+
+	btor(ZR36057_JPC_Active, ZR36057_JPC);	// /Active
+	btor(ZR36057_JMC_Go_en, ZR36057_JMC);	// /Go_en
+	udelay(30);
+	set_frame(zr, 1);	// /FRAME
+}
+
+static void zr36057_enable_jpg(struct zoran *zr,
+			       enum zoran_codec_mode mode)
+{
+	static int zero = 0;
+	static int one = 1;
+
+	zr->codec_mode = mode;
+	switch (mode) {
+
+	case BUZ_MODE_MOTION_COMPRESS:
+		set_videobus_enable(zr, 0);
+		set_videobus_dir(zr, 0);	// GPIO(zr, 1, 0);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+				   DECODER_ENABLE_OUTPUT, &one);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+				   ENCODER_SET_INPUT, &zero);
+		set_videobus_enable(zr, 1);
+		zr36060_sleep(zr, 0);
+		zr36060_set_cap(zr, mode);	// Load ZR36060
+		init_jpeg_queue(zr);
+		zr36057_set_jpg(zr, mode);	// \P_Reset, ... Video param, FIFO
+
+		clear_interrupt_counters(zr);
+		DEBUG1(printk
+		       (KERN_INFO "%s: enable_jpg MOTION_COMPRESS\n",
+			zr->name));
+		break;
+
+	case BUZ_MODE_MOTION_DECOMPRESS:
+		set_videobus_enable(zr, 0);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+				   DECODER_ENABLE_OUTPUT, &zero);
+		set_videobus_dir(zr, 1);	// GPIO(zr, 1, 1);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+				   ENCODER_SET_INPUT, &one);
+		set_videobus_enable(zr, 1);
+		zr36060_sleep(zr, 0);
+		zr36060_set_cap(zr, mode);	// Load ZR36060
+		init_jpeg_queue(zr);
+		zr36057_set_jpg(zr, mode);	// \P_Reset, ... Video param, FIFO
+
+		clear_interrupt_counters(zr);
+		DEBUG1(printk
+		       (KERN_INFO "%s: enable_jpg MOTION_DECOMPRESS\n",
+			zr->name));
+		break;
+
+	case BUZ_MODE_IDLE:
+	default:
+		/* shut down processing */
+		btand(~(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ),
+		      ZR36057_ICR);
+		btwrite(cardjpegint[zr->card] | ZR36057_ICR_JPEGRepIRQ,
+			ZR36057_ISR);
+		btand(~ZR36057_JMC_Go_en, ZR36057_JMC);	// \Go_en
+
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(HZ/20);
+
+		set_videobus_dir(zr, 0);	// GPIO(zr, 1, 0);
+		set_frame(zr, 1);	//GPIO(zr, 6, 1);     // /FRAME
+		btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);	// /CFlush
+		btwrite(0, ZR36057_JPC);	// \P_Reset,\CodTrnsEn,\Active
+		btand(~ZR36057_JMC_VFIFO_FB, ZR36057_JMC);
+		btand(~ZR36057_JMC_SyncMstr, ZR36057_JMC);
+		zr36060_reset(zr);
+		zr36060_sleep(zr, 1);
+		zr36057_adjust_vfe(zr, mode);
+		set_videobus_enable(zr, 0);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+				   DECODER_ENABLE_OUTPUT, &one);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+				   ENCODER_SET_INPUT, &zero);
+		set_videobus_enable(zr, 1);
+		DEBUG1(printk
+		       (KERN_INFO "%s: enable_jpg IDLE\n", zr->name));
+		break;
+
+	}
+}
+
+/*
+ *   Queue a MJPEG buffer for capture/playback
+ */
+
+static int jpg_qbuf(struct zoran *zr, int frame,
+		    enum zoran_codec_mode mode)
+{
+	unsigned long flags;
+	int res;
+
+	/* Check if buffers are allocated */
+
+	if (!zr->jpg_buffers_allocated) {
+		printk(KERN_ERR
+		       "%s: jpg_qbuf: buffers not yet allocated\n",
+		       zr->name);
+		return -ENOMEM;
+	}
+
+	/* Does the user want to stop streaming? */
+
+	if (frame < 0) {
+		if (zr->codec_mode == mode) {
+			zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+			return 0;
+		} else {
+			printk(KERN_ERR
+			       "%s: jpg_qbuf - stop streaming but not in streaming mode\n",
+			       zr->name);
+			return -EINVAL;
+		}
+	}
+
+	/* No grabbing outside the buffer range! */
+
+	if (frame >= zr->jpg_nbufs) {
+		printk(KERN_ERR "%s: jpg_qbuf: buffer %d out of range\n",
+		       zr->name, frame);
+		return -EINVAL;
+	}
+
+	/* what is the codec mode right now? */
+
+	if (zr->codec_mode == BUZ_MODE_IDLE) {
+		/* Ok load up the zr36060 */
+		zr36057_enable_jpg(zr, mode);
+	} else if (zr->codec_mode != mode) {
+		/* wrong codec mode active - invalid */
+		printk(KERN_ERR "%s: jpg_qbuf - codec in wrong mode\n",
+		       zr->name);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&zr->lock, flags);
+
+	/* make sure a grab isn't going on currently with this buffer */
+
+	switch (zr->jpg_gbuf[frame].state) {
+
+	case BUZ_STATE_DONE:
+		DEBUG1(printk
+		       (KERN_WARNING
+			"%s: Warning: queing frame in BUZ_STATE_DONE state\n",
+			zr->name));
+	case BUZ_STATE_USER:
+		/* since there is at least one unused buffer there's room for at least one more pend[] entry */
+		zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = frame;
+		zr->jpg_gbuf[frame].state = BUZ_STATE_PEND;
+		zoran_feed_stat_com(zr);
+		res = 0;
+		break;
+
+	default:
+	case BUZ_STATE_DMA:
+	case BUZ_STATE_PEND:
+		res = -EBUSY;	/* what are you doing? */
+		break;
+
+	}
+
+	spin_unlock_irqrestore(&zr->lock, flags);
+
+	/* Start the zr36060 when the first frame is queued  */
+	if (zr->jpg_que_head == 1)
+		jpeg_start(zr);
+
+	return res;
+}
+
+/*
+ *   Sync on a MJPEG buffer
+ */
+
+static int jpg_sync(struct zoran *zr, struct zoran_sync *bs)
+{
+	unsigned long flags;
+	int frame, timeout;
+
+	if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS
+	    && zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+		DEBUG1(printk(KERN_ERR
+		       "%s: BUZIOCSYNC: - codec not in streaming mode\n",
+		       zr->name));
+		return -EINVAL;
+	}
+	while (zr->jpg_que_tail == zr->jpg_dma_tail) {
+		if (zr->jpg_dma_tail == zr->jpg_dma_head)
+			break;
+		timeout =
+		    interruptible_sleep_on_timeout(&zr->jpg_capq, 10 * HZ);
+		if (!timeout) {
+			btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+			udelay(1);
+			printk(KERN_ERR
+			       "%s: timeout: codec isr=0x%02x, csr=0x%02x\n",
+			       zr->name, zr36060_read_8(zr, 0x008),
+			       zr36060_read_8(zr, 0x001));
+			return -ETIME;
+		} else if (signal_pending(current))
+			return -ERESTARTSYS;
+	}
+
+	spin_lock_irqsave(&zr->lock, flags);
+
+	if (zr->jpg_dma_tail != zr->jpg_dma_head)
+		frame = zr->jpg_pend[zr->jpg_que_tail++ & BUZ_MASK_FRAME];
+	else
+		frame = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+	/* buffer should now be in BUZ_STATE_DONE */
+
+#if(DEBUGLEVEL > 0)
+	if (zr->jpg_gbuf[frame].state != BUZ_STATE_DONE)
+		printk(KERN_ERR "%s: jpg_sync - internal error\n",
+		       zr->name);
+#endif
+
+	*bs = zr->jpg_gbuf[frame].bs;
+	zr->jpg_gbuf[frame].state = BUZ_STATE_USER;
+
+	spin_unlock_irqrestore(&zr->lock, flags);
+
+	return 0;
+}
+
+/* when this is called the spinlock must be held */
+static void zoran_feed_stat_com(struct zoran *zr)
+{
+	/* move frames from pending queue to DMA */
+
+	int frame, i, max_stat_com;
+
+	max_stat_com =
+	    (zr->params.TmpDcm ==
+	     1) ? BUZ_NUM_STAT_COM : (BUZ_NUM_STAT_COM >> 1);
+
+	while ((zr->jpg_dma_head - zr->jpg_dma_tail) < max_stat_com
+	       && zr->jpg_dma_head < zr->jpg_que_head) {
+
+		frame = zr->jpg_pend[zr->jpg_dma_head & BUZ_MASK_FRAME];
+		if (zr->params.TmpDcm == 1) {
+			/* fill 1 stat_com entry */
+			i = (zr->jpg_dma_head -
+			     zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+			if (!(zr->stat_com[i] & 1))
+				break;
+			zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
+		} else {
+			/* fill 2 stat_com entries */
+			i = ((zr->jpg_dma_head -
+			      zr->jpg_err_shift) & 1) * 2;
+			if (!(zr->stat_com[i] & 1))
+				break;
+			zr->stat_com[i] = zr->jpg_gbuf[frame].frag_tab_bus;
+			zr->stat_com[i + 1] =
+			    zr->jpg_gbuf[frame].frag_tab_bus;
+		}
+		zr->jpg_gbuf[frame].state = BUZ_STATE_DMA;
+		zr->jpg_dma_head++;
+
+	}
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+		zr->jpg_queued_num++;
+}
+
+/* when this is called the spinlock must be held */
+static void zoran_reap_stat_com(struct zoran *zr)
+{
+	/* move frames from DMA queue to done queue */
+
+	int i;
+	u32 stat_com;
+	unsigned int seq;
+	unsigned int dif;
+	int frame;
+	struct zoran_gbuffer *gbuf;
+
+	/* In motion decompress we don't have a hardware frame counter,
+	   we just count the interrupts here */
+
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+		zr->jpg_seq_num++;
+	}
+	while (zr->jpg_dma_tail < zr->jpg_dma_head) {
+		if (zr->params.TmpDcm == 1)
+			i = (zr->jpg_dma_tail -
+			     zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+		else
+			i = ((zr->jpg_dma_tail -
+			      zr->jpg_err_shift) & 1) * 2 + 1;
+
+		stat_com = zr->stat_com[i];
+
+		if ((stat_com & 1) == 0) {
+			return;
+		}
+		frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+		gbuf = &zr->jpg_gbuf[frame];
+		get_fast_time(&gbuf->bs.timestamp);
+
+		if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+			gbuf->bs.length = (stat_com & 0x7fffff) >> 1;
+
+			/* update sequence number with the help of the counter in stat_com */
+
+			seq = ((stat_com >> 24) + zr->jpg_err_seq) & 0xff;
+			dif = (seq - zr->jpg_seq_num) & 0xff;
+			zr->jpg_seq_num += dif;
+		} else {
+			gbuf->bs.length = 0;
+		}
+		gbuf->bs.seq =
+		    zr->params.TmpDcm ==
+		    2 ? (zr->jpg_seq_num >> 1) : zr->jpg_seq_num;
+		gbuf->state = BUZ_STATE_DONE;
+
+		zr->jpg_dma_tail++;
+	}
+}
+
+static void error_handler(struct zoran *zr, u32 astat, u32 stat)
+{
+	/* This is JPEG error handling part */
+	if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS)
+	    && (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) {
+		//printk(KERN_ERR "%s: Internal error: error handling request in mode %d\n", zr->name, zr->codec_mode);
+		return;
+	}
+	if ((stat & 1) == 0
+	    && zr->codec_mode == BUZ_MODE_MOTION_COMPRESS
+	    && zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_nbufs) {
+		/* No free buffers... */
+		zoran_reap_stat_com(zr);
+		zoran_feed_stat_com(zr);
+		wake_up_interruptible(&zr->jpg_capq);
+		zr->JPEG_missed = 0;
+		return;
+	}
+	if (zr->JPEG_error != 1) {
+		/*
+		 * First entry: error just happened during normal operation
+		 * 
+		 * In BUZ_MODE_MOTION_COMPRESS:
+		 * 
+		 * Possible glitch in TV signal. In this case we should
+		 * stop the codec and wait for good quality signal before
+		 * restarting it to avoid further problems
+		 * 
+		 * In BUZ_MODE_MOTION_DECOMPRESS:
+		 * 
+		 * Bad JPEG frame: we have to mark it as processed (codec crashed
+		 * and was not able to do it itself), and to remove it from queue.
+		 */
+		btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+		udelay(1);
+		stat =
+		    stat | (post_office_read(zr, 7, 0) & 3) << 8 |
+		    zr36060_read_8(zr, 0x008);
+		btwrite(0, ZR36057_JPC);
+		btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+		zr36060_reset(zr);
+		zr36060_sleep(zr, 1);
+		zr->JPEG_error = 1;
+		zr->num_errors++;
+		/* Report error */
+#if(DEBUGLEVEL > 1)
+		if (zr->num_errors <= 8) {
+			long frame;
+			frame =
+			    zr->jpg_pend[zr->
+					 jpg_dma_tail & BUZ_MASK_FRAME];
+			printk(KERN_ERR
+			       "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+			       zr->name, stat, zr->last_isr,
+			       zr->jpg_que_tail, zr->jpg_dma_tail,
+			       zr->jpg_dma_head, zr->jpg_que_head,
+			       zr->jpg_seq_num, frame);
+			printk("stat_com frames:");
+			{
+				int i, j;
+				for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+					for (i = 0; i < zr->jpg_nbufs; i++) {
+						if (zr->stat_com[j] ==
+						    zr->jpg_gbuf[i].
+						    frag_tab_bus) {
+							printk("% d->%d",
+							       j, i);
+						}
+					}
+				}
+				printk("\n");
+			}
+		}
+#endif
+		/* Find an entry in stat_com and rotate contents */
+		{
+			int i;
+
+			if (zr->params.TmpDcm == 1)
+				i = (zr->jpg_dma_tail -
+				     zr->
+				     jpg_err_shift) & BUZ_MASK_STAT_COM;
+			else
+				i = ((zr->jpg_dma_tail -
+				      zr->jpg_err_shift) & 1) * 2;
+			if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+				/* Mimic zr36067 operation */
+				zr->stat_com[i] |= 1;
+				if (zr->params.TmpDcm != 1)
+					zr->stat_com[i + 1] |= 1;
+				/* Refill */
+				zoran_reap_stat_com(zr);
+				zoran_feed_stat_com(zr);
+				wake_up_interruptible(&zr->jpg_capq);
+				/* Find an entry in stat_com again after refill */
+				if (zr->params.TmpDcm == 1)
+					i = (zr->jpg_dma_tail -
+					     zr->
+					     jpg_err_shift) &
+					    BUZ_MASK_STAT_COM;
+				else
+					i = ((zr->jpg_dma_tail -
+					      zr->jpg_err_shift) & 1) * 2;
+			}
+			if (i) {
+				/* Rotate stat_comm entries to make current entry first */
+				int j;
+				u32 bus_addr[BUZ_NUM_STAT_COM];
+
+				memcpy(bus_addr, zr->stat_com,
+				       sizeof(bus_addr));
+				for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+					zr->stat_com[j] =
+					    bus_addr[(i +
+						      j) &
+						     BUZ_MASK_STAT_COM];
+				}
+				zr->jpg_err_shift += i;
+				zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
+			}
+			if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+				zr->jpg_err_seq = zr->jpg_seq_num;	/* + 1; */
+		}
+	}
+	/* Now the stat_comm buffer is ready for restart */
+	{
+		int status;
+
+		status = 0;
+		if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_GET_STATUS, &status);
+		if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+		    || (status & DECODER_STATUS_GOOD)) {
+	    /********** RESTART code *************/
+			zr36060_reset(zr);
+			zr36060_set_cap(zr, zr->codec_mode);
+			zr36057_set_jpg(zr, zr->codec_mode);
+			jpeg_start(zr);
+#if(DEBUGLEVEL > 1)
+			if (zr->num_errors <= 8)
+				printk(KERN_INFO "%s: Restart\n",
+				       zr->name);
+#endif
+			zr->JPEG_missed = 0;
+			zr->JPEG_error = 2;
+	    /********** End RESTART code ***********/
+		}
+	}
+}
+
+static void zoran_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	u32 stat, astat;
+	int count;
+	struct zoran *zr;
+	unsigned long flags;
+
+	zr = (struct zoran *) dev_id;
+	count = 0;
+
+	if (zr->testing) {
+		/* Testing interrupts */
+		spin_lock_irqsave(&zr->lock, flags);
+		while ((stat = count_reset_interrupt(zr))) {
+			if (count++ > 100) {
+				btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+				printk(KERN_ERR
+				       "%s: IRQ lockup while testing, isr=0x%08x, cleared int mask\n",
+				       zr->name, stat);
+				wake_up_interruptible(&zr->test_q);
+			}
+		}
+		zr->last_isr = stat;
+		spin_unlock_irqrestore(&zr->lock, flags);
+		return;
+	}
+
+	spin_lock_irqsave(&zr->lock, flags);
+	while (1) {
+		/* get/clear interrupt status bits */
+		stat = count_reset_interrupt(zr);
+		astat = stat & IRQ_MASK;
+		if (!astat) {
+			break;
+		}
+		if (astat & cardvsync[zr->card]) {	// SW
+
+			if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+			    || zr->codec_mode ==
+			    BUZ_MODE_MOTION_COMPRESS) {
+				/* count missed interrupts */
+				zr->JPEG_missed++;
+			}
+			//post_office_read(zr,1,0);
+			/* Interrupts may still happen when zr->v4l_memgrab_active is switched off.
+			   We simply ignore them */
+
+			if (zr->v4l_memgrab_active) {
+
+				/* A lot more checks should be here ... */
+				if ((btread(ZR36057_VSSFGR) &
+				     ZR36057_VSSFGR_SnapShot) == 0)
+					printk(KERN_WARNING
+					       "%s: BuzIRQ with SnapShot off ???\n",
+					       zr->name);
+
+				if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+					/* There is a grab on a frame going on, check if it has finished */
+
+					if ((btread(ZR36057_VSSFGR) &
+					     ZR36057_VSSFGR_FrameGrab) ==
+					    0) {
+						/* it is finished, notify the user */
+
+						zr->v4l_gbuf[zr->
+							     v4l_grab_frame].
+						    state = BUZ_STATE_DONE;
+						zr->v4l_grab_frame =
+						    NO_GRAB_ACTIVE;
+						zr->v4l_grab_seq++;
+						zr->v4l_pend_tail++;
+					}
+				}
+
+				if (zr->v4l_grab_frame == NO_GRAB_ACTIVE)
+					wake_up_interruptible(&zr->
+							      v4l_capq);
+
+				/* Check if there is another grab queued */
+
+				if (zr->v4l_grab_frame == NO_GRAB_ACTIVE
+				    && zr->v4l_pend_tail !=
+				    zr->v4l_pend_head) {
+
+					int frame =
+					    zr->v4l_pend[zr->
+							 v4l_pend_tail &
+							 V4L_MASK_FRAME];
+					u32 reg;
+
+					zr->v4l_grab_frame = frame;
+
+					/* Set zr36057 video front end and enable video */
+
+					/* Buffer address */
+
+					reg =
+					    zr->v4l_gbuf[frame].
+					    fbuffer_bus;
+					btwrite(reg, ZR36057_VDTR);
+					if (zr->video_interlace)
+						reg += zr->gbpl;
+					btwrite(reg, ZR36057_VDBR);
+
+					/* video stride, status, and frame grab register */
+
+					reg = 0;
+					if (zr->video_interlace)
+						reg += zr->gbpl;
+					reg =
+					    (reg <<
+					     ZR36057_VSSFGR_DispStride);
+					reg |= ZR36057_VSSFGR_VidOvf;
+					reg |= ZR36057_VSSFGR_SnapShot;
+					reg |= ZR36057_VSSFGR_FrameGrab;
+					btwrite(reg, ZR36057_VSSFGR);
+
+					btor(ZR36057_VDCR_VidEn,
+					     ZR36057_VDCR);
+				}
+			}
+		}
+#if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+		if (astat & ZR36057_ISR_CodRepIRQ) {
+			zr->intr_counter_CodRepIRQ++;
+			IDEBUG(printk
+			       (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+				zr->name));
+			btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+		}
+#endif				/* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+#if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+		if (astat & ZR36057_ISR_JPEGRepIRQ) {
+
+			if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS
+			    || zr->codec_mode ==
+			    BUZ_MODE_MOTION_COMPRESS) {
+#if(DEBUGLEVEL > 1)
+				if (!zr->frame_num || zr->JPEG_error) {
+					printk(KERN_INFO
+					       "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+					       zr->name, stat,
+					       zr->params.odd_even,
+					       zr->params.field_per_buff,
+					       zr->JPEG_missed);
+					{
+						char sc[] = "0000";
+						char sv[5];
+						int i;
+						strcpy(sv, sc);
+						for (i = 0; i < 4; i++) {
+							if (zr->
+							    stat_com[i] &
+							    1)
+								sv[i] =
+								    '1';
+						}
+						sv[4] = 0;
+						printk(KERN_INFO
+						       "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+						       zr->name, sv,
+						       zr->jpg_que_tail,
+						       zr->jpg_dma_tail,
+						       zr->jpg_dma_head,
+						       zr->jpg_que_head);
+					}
+				} else {
+					if (zr->JPEG_missed > zr->JPEG_max_missed)	// Get statistics
+						zr->JPEG_max_missed =
+						    zr->JPEG_missed;
+					if (zr->JPEG_missed <
+					    zr->JPEG_min_missed)
+						zr->JPEG_min_missed =
+						    zr->JPEG_missed;
+				}
+#endif
+#if(DEBUGLEVEL > 2)
+				if (zr->frame_num < 6) {
+					int i;
+					printk("%s: seq=%ld stat_com:",
+					       zr->name, zr->jpg_seq_num);
+					for (i = 0; i < 4; i++) {
+						printk(" %08x",
+						       zr->stat_com[i]);
+					}
+					printk("\n");
+				}
+#endif
+				zr->frame_num++;
+				zr->JPEG_missed = 0;
+				zr->JPEG_error = 0;
+				zoran_reap_stat_com(zr);
+				zoran_feed_stat_com(zr);
+				wake_up_interruptible(&zr->jpg_capq);
+			}	//else {
+			//      printk(KERN_ERR "%s: JPEG interrupt while not in motion (de)compress mode!\n", zr->name);
+			//}
+		}
+#endif				/* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+		if ((astat & cardjpegint[zr->card])	/* DATERR interrupt received                 */
+		    ||zr->JPEG_missed > 25	/* Too many fields missed without processing */
+		    || zr->JPEG_error == 1	/* We are already in error processing        */
+		    || ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+			&& (zr->
+			    frame_num & (zr->JPEG_missed >
+					 zr->params.field_per_buff)))
+		    /* fields missed during decompression */
+		    ) {
+			error_handler(zr, astat, stat);
+		}
+
+		count++;
+		if (count > 10) {
+			printk(KERN_WARNING "%s: irq loop %d\n", zr->name,
+			       count);
+			if (count > 20) {
+				btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+				printk(KERN_ERR
+				       "%s: IRQ lockup, cleared int mask\n",
+				       zr->name);
+				break;
+			}
+		}
+		zr->last_isr = stat;
+	}
+	spin_unlock_irqrestore(&zr->lock, flags);
+}
+
+/* Check a zoran_params struct for correctness, insert default params */
+
+static int zoran_check_params(struct zoran *zr,
+			      struct zoran_params *params)
+{
+	int err = 0, err0 = 0;
+
+	/* insert constant params */
+
+	params->major_version = MAJOR_VERSION;
+	params->minor_version = MINOR_VERSION;
+
+	/* Check input and norm: must be set by calling VIDIOCSCHAN only! */
+
+	params->norm = zr->params.norm;
+	params->input = zr->params.input;
+
+	/* Check decimation, set default values for decimation = 1, 2, 4 */
+
+	switch (params->decimation) {
+	case 1:
+
+		params->HorDcm = 1;
+		params->VerDcm = 1;
+		params->TmpDcm = 1;
+		params->field_per_buff = 2;
+
+		params->img_x = 0;
+		params->img_y = 0;
+		params->img_width = zr->timing->Wa;
+		params->img_height = zr->timing->Ha / 2;
+		break;
+
+	case 2:
+
+		params->HorDcm = 2;
+		params->VerDcm = 1;
+		params->TmpDcm = 2;
+		params->field_per_buff = 1;
+
+		params->img_x = 8;
+		params->img_y = 0;
+		params->img_width = zr->timing->Wa;
+		params->img_height = zr->timing->Ha / 2;
+		break;
+
+	case 4:
+
+		params->HorDcm = 4;
+		params->VerDcm = 2;
+		params->TmpDcm = 2;
+		params->field_per_buff = 1;
+
+		params->img_x = 8;
+		params->img_y = 0;
+		params->img_width = zr->timing->Wa;
+		params->img_height = zr->timing->Ha / 2;
+		break;
+
+	case 0:
+
+		/* We have to check the data the user has set */
+
+		if (params->HorDcm != 1 && params->HorDcm != 2
+		    && params->HorDcm != 4)
+			err0++;
+		if (params->VerDcm != 1 && params->VerDcm != 2)
+			err0++;
+		if (params->TmpDcm != 1 && params->TmpDcm != 2)
+			err0++;
+		if (params->field_per_buff != 1
+		    && params->field_per_buff != 2)
+			err0++;
+
+		if (params->img_x < 0)
+			err0++;
+		if (params->img_y < 0)
+			err0++;
+		if (params->img_width < 0)
+			err0++;
+		if (params->img_height < 0)
+			err0++;
+		if (params->img_x + params->img_width > zr->timing->Wa)
+			err0++;
+		if (params->img_y + params->img_height >
+		    zr->timing->Ha / 2)
+			err0++;
+		if (params->HorDcm) {
+			if (params->img_width % (16 * params->HorDcm) != 0)
+				err0++;
+			if (params->img_height % (8 * params->VerDcm) != 0)
+				err0++;
+		}
+
+		if (err0) {
+			DEBUG1(printk(KERN_ERR
+			       "%s: SET PARAMS: error in params for decimation = 0\n",
+			       zr->name));
+			err++;
+		}
+		break;
+
+	default:
+		DEBUG1(printk(KERN_ERR
+		       "%s: SET PARAMS: decimation = %d, must be 0, 1, 2 or 4\n",
+		       zr->name, params->decimation));
+		err++;
+		break;
+	}
+
+	if (params->quality > 100)
+		params->quality = 100;
+	if (params->quality < 5)
+		params->quality = 5;
+
+	if (params->APPn < 0)
+		params->APPn = 0;
+	if (params->APPn > 15)
+		params->APPn = 15;
+	if (params->APP_len < 0)
+		params->APP_len = 0;
+	if (params->APP_len > 60)
+		params->APP_len = 60;
+	if (params->COM_len < 0)
+		params->COM_len = 0;
+	if (params->COM_len > 60)
+		params->COM_len = 60;
+
+	if (err)
+		return -EINVAL;
+
+	return 0;
+
+}
+static void zoran_open_init_params(struct zoran *zr)
+{
+	int i;
+
+	/* Per default, map the V4L Buffers */
+
+	zr->map_mjpeg_buffers = 0;
+
+	/* User must explicitly set a window */
+
+	zr->window_set = 0;
+
+	zr->window.x = 0;
+	zr->window.y = 0;
+	zr->window.width = 0;
+	zr->window.height = 0;
+	zr->window.chromakey = 0;
+	zr->window.flags = 0;
+	zr->window.clips = NULL;
+	zr->window.clipcount = 0;
+
+	zr->video_interlace = 0;
+
+	zr->v4l_memgrab_active = 0;
+	zr->v4l_overlay_active = 0;
+
+	zr->v4l_grab_frame = NO_GRAB_ACTIVE;
+	zr->v4l_grab_seq = 0;
+
+	zr->gwidth = 0;
+	zr->gheight = 0;
+	zr->gformat = 0;
+	zr->gbpl = 0;
+
+	/* DMA ring stuff for V4L */
+
+	zr->v4l_pend_tail = 0;
+	zr->v4l_pend_head = 0;
+	for (i = 0; i < v4l_nbufs; i++) {
+		zr->v4l_gbuf[i].state = BUZ_STATE_USER;	/* nothing going on */
+	}
+
+	/* Set necessary params and call zoran_check_params to set the defaults */
+
+	zr->params.decimation = 1;
+
+	zr->params.quality = 50;	/* default compression factor 8 */
+	if (zr->card != BUZ)
+		zr->params.odd_even = 1;
+	else
+		zr->params.odd_even = 0;
+
+	zr->params.APPn = 0;
+	zr->params.APP_len = 0;	/* No APPn marker */
+	for (i = 0; i < 60; i++)
+		zr->params.APP_data[i] = 0;
+
+	zr->params.COM_len = 0;	/* No COM marker */
+	for (i = 0; i < 60; i++)
+		zr->params.COM_data[i] = 0;
+
+	zr->params.VFIFO_FB = 0;
+
+	memset(zr->params.reserved, 0, sizeof(zr->params.reserved));
+
+	zr->params.jpeg_markers = JPEG_MARKER_DHT | JPEG_MARKER_DQT;
+
+	i = zoran_check_params(zr, &zr->params);
+	if (i)
+		printk(KERN_ERR
+		       "%s: zoran_open_init_params internal error\n",
+		       zr->name);
+
+	clear_interrupt_counters(zr);
+	zr->testing = 0;
+}
+
+/*
+ *   Open a zoran card. Right now the flags stuff is just playing
+ */
+
+static int zoran_open(struct video_device *dev, int flags)
+{
+	struct zoran *zr = (struct zoran *) dev;
+	//int one = 1;
+
+	DEBUG1(printk
+	       (KERN_INFO "%s: zoran_open, %s pid=[%d]\n", zr->name,
+		current->comm, current->pid));
+
+	switch (flags) {
+
+	case 0:
+		if (zr->user > 1) {
+			DEBUG1(printk(KERN_WARNING
+			       "%s: zoran_open: Buz is allready in use\n",
+			       zr->name));
+			return -EBUSY;
+		}
+		zr->user++;
+
+		if (zr->user == 1 && v4l_fbuffer_alloc(zr) < 0) {
+			zr->user--;
+			printk(KERN_ERR
+			       "%s: zoran_open: v4l_fbuffer_alloc failed\n",
+			       zr->name);
+			return -ENOMEM;
+		}
+
+		/* default setup */
+
+		if (zr->user == 1) {	/* First device open */
+			zoran_open_init_params(zr);
+
+			zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+
+			btwrite(IRQ_MASK, ZR36057_ISR);	// Clears interrupts
+			btor(ZR36057_ICR_IntPinEn, ZR36057_ICR);
+			dev->busy = 0;	/* Allow second open */
+		}
+
+		break;
+
+	default:
+		DEBUG1(printk(KERN_WARNING
+		       "%s: zoran_open: flags = 0x%x not yet supported\n",
+		       zr->name, flags));
+		return -EBUSY;
+		break;
+
+	}
+	MOD_INC_USE_COUNT;
+	return 0;
+}
+
+static void zoran_close(struct video_device *dev)
+{
+	struct zoran *zr = (struct zoran *) dev;
+	int zero = 0, two = 2;
+
+	DEBUG1(printk
+	       (KERN_INFO "%s: zoran_close, %s pid=[%d]\n", zr->name,
+		current->comm, current->pid));
+	/* Clean up JPEG process */
+
+	wake_up_interruptible(&zr->jpg_capq);
+	zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+	jpg_fbuffer_free(zr);
+	zr->jpg_nbufs = 0;
+
+	if (zr->user == 1) {	/* Last process */
+		/* disable interrupts */
+		btand(~ZR36057_ICR_IntPinEn, ZR36057_ICR);
+
+#if(DEBUGLEVEL > 1)
+		print_interrupts(zr);
+#endif
+		/* Overlay off */
+		wake_up_interruptible(&zr->v4l_capq);
+		zr36057_set_memgrab(zr, 0);
+		if (zr->v4l_overlay_active)
+			zr36057_overlay(zr, 0);
+		v4l_fbuffer_free(zr);
+
+		if (!pass_through) {	/* Switch to color bar */
+			set_videobus_enable(zr, 0);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_ENABLE_OUTPUT, &zero);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEOENCODER,
+					   ENCODER_SET_INPUT, &two);
+			set_videobus_enable(zr, 1);
+		}
+	}
+
+	zr->user--;
+
+	MOD_DEC_USE_COUNT;
+	DEBUG2(printk(KERN_INFO "%s: zoran_close done\n", zr->name));
+}
+
+
+static long zoran_read(struct video_device *dev, char *buf,
+		       unsigned long count, int nonblock)
+{
+	return -EINVAL;
+}
+
+static long zoran_write(struct video_device *dev, const char *buf,
+			unsigned long count, int nonblock)
+{
+	return -EINVAL;
+}
+
+/*
+ *   ioctl routine
+ */
+
+static int do_zoran_ioctl(struct zoran *zr, unsigned int cmd,
+		       void *arg)
+{
+	switch (cmd) {
+
+	case VIDIOCGCAP:
+		{
+			struct video_capability b;
+			DEBUG2(printk("%s: ioctl VIDIOCGCAP\n", zr->name));
+
+			strncpy(b.name, zr->video_dev.name,
+				sizeof(b.name));
+			b.type =
+			    VID_TYPE_CAPTURE | VID_TYPE_OVERLAY |
+			    VID_TYPE_CLIPPING | VID_TYPE_FRAMERAM |
+			    VID_TYPE_SCALES;
+			/* theoretically we could also flag VID_TYPE_SUBCAPTURE
+			   but this is not even implemented in the BTTV driver */
+
+			if (zr->card == DC10 || zr->card == DC10plus) {
+				b.channels = 3;	/* composite, svhs, internal */
+			} else {
+				b.channels = 2;	/* composite, svhs */
+			}
+			b.audios = 0;
+			b.maxwidth = BUZ_MAX_WIDTH;
+			b.maxheight = BUZ_MAX_HEIGHT;
+			b.minwidth = BUZ_MIN_WIDTH;
+			b.minheight = BUZ_MIN_HEIGHT;
+			if (copy_to_user(arg, &b, sizeof(b))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case VIDIOCGCHAN:
+		{
+			struct video_channel v;
+
+			if (copy_from_user(&v, arg, sizeof(v))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCGCHAN for channel %d\n",
+				zr->name, v.channel));
+			switch (v.channel) {
+			case 0:
+				strcpy(v.name, "Composite");
+				break;
+			case 1:
+				strcpy(v.name, "SVHS");
+				break;
+			case 2:
+				if (zr->card == DC10
+				    || zr->card == DC10plus) {
+					strcpy(v.name, "Internal/comp");
+					break;
+				}
+			default:
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCGCHAN on not existing channel %d\n",
+				       zr->name, v.channel));
+				return -EINVAL;
+			}
+			v.tuners = 0;
+			v.flags = 0;
+			v.type = VIDEO_TYPE_CAMERA;
+			v.norm = zr->params.norm;
+			if (copy_to_user(arg, &v, sizeof(v))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+		/* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
+
+		 * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
+		 *                                 ^^^^^^^
+		 * The famos BTTV driver has it implemented with a struct video_channel argument
+		 * and we follow it for compatibility reasons
+		 *
+		 * BTW: this is the only way the user can set the norm!
+		 */
+
+	case VIDIOCSCHAN:
+		{
+			struct video_channel v;
+			int input;
+			int on, res;
+			int encoder_norm;
+
+			if (copy_from_user(&v, arg, sizeof(v))) {
+				return -EFAULT;
+			}
+
+			if (zr->codec_mode != BUZ_MODE_IDLE) {
+				if (v.norm != zr->params.norm
+				    || v.channel != zr->params.input) {
+					DEBUG1(printk(KERN_ERR
+					       "%s: VIDIOCSCHAN called while the card in capture/playback mode\n",
+					       zr->name));
+					return -EINVAL;
+				} else {
+					DEBUG1(printk(BUZ_WARNING
+					       "%s: Warning: VIDIOCSCHAN called while the card in capture/playback mode\n",
+					       zr->name));
+				}
+			}
+
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCSCHAN: channel=%d, norm=%d\n",
+				zr->name, v.channel, v.norm));
+			switch (v.channel) {
+			case 0:
+				if (zr->card == BUZ)
+					input = 3;
+				else
+					input = 0;
+				break;
+			case 1:
+				input = 7;
+				break;
+			case 2:
+				if (zr->card == DC10
+				    || zr->card == DC10plus) {
+					input = 5;
+					break;
+				}
+			default:
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSCHAN on not existing channel %d\n",
+				       zr->name, v.channel));
+				return -EINVAL;
+				break;
+			}
+
+			if (lock_norm && v.norm != zr->params.norm) {
+				if (lock_norm > 1) {
+					DEBUG1(printk(KERN_WARNING
+					       "%s: VIDIOCSCHAN: TV standard is locked, can not switch norm.\n",
+					       zr->name));
+					return -EINVAL;
+				} else {
+					DEBUG1(printk(KERN_WARNING
+					       "%s: VIDIOCSCHAN: TV standard is locked, norm was not changed.\n",
+					       zr->name));
+					v.norm = zr->params.norm;
+				}
+			}
+			
+			if(v.norm >= 2)
+				return -EINVAL;
+
+			if (!cardnorms[zr->card][v.norm]) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSCHAN with not supported norm %d\n",
+				       zr->name, v.norm));
+				return -EOPNOTSUPP;
+				break;
+			}
+			encoder_norm = v.norm;
+
+			zr->params.norm = v.norm;
+			zr->params.input = v.channel;
+			zr->timing = cardnorms[zr->card][zr->params.norm];
+
+			/* We switch overlay off and on since a change in the norm
+			   needs different VFE settings */
+
+			on = zr->v4l_overlay_active
+			    && !zr->v4l_memgrab_active;
+			if (on)
+				zr36057_overlay(zr, 0);
+
+			set_videobus_enable(zr, 0);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_INPUT, &input);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_NORM,
+					   &zr->params.norm);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEOENCODER,
+					   ENCODER_SET_NORM,
+					   &encoder_norm);
+			set_videobus_enable(zr, 1);
+
+			if (on)
+				zr36057_overlay(zr, 1);
+
+			/* Make sure the changes come into effect */
+			res = wait_grab_pending(zr);
+			if (res)
+				return res;
+
+			return 0;
+		}
+		break;
+
+	case VIDIOCGTUNER:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCGTUNER not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCSTUNER:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCSTUNER not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCGPICT:
+		{
+			struct video_picture p = zr->picture;
+
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCGPICT\n", zr->name));
+			p.depth = zr->buffer.depth;
+			switch (zr->buffer.depth) {
+			case 15:
+				p.palette = VIDEO_PALETTE_RGB555;
+				break;
+
+			case 16:
+				p.palette = VIDEO_PALETTE_RGB565;
+				break;
+
+			case 24:
+				p.palette = VIDEO_PALETTE_RGB24;
+				break;
+
+			case 32:
+				p.palette = VIDEO_PALETTE_RGB32;
+				break;
+			}
+
+			if (copy_to_user(arg, &p, sizeof(p))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case VIDIOCSPICT:
+		{
+			struct video_picture p;
+
+			if (copy_from_user(&p, arg, sizeof(p))) {
+				return -EFAULT;
+			}
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_PICTURE, &p);
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCSPICT bri=%d hue=%d col=%d con=%d dep=%d pal=%d\n",
+				zr->name, p.brightness, p.hue, p.colour,
+				p.contrast, p.depth, p.palette));
+			/* The depth and palette values have no meaning to us,
+			   should we return  -EINVAL if they don't fit ? */
+			zr->picture = p;
+			return 0;
+		}
+		break;
+
+	case VIDIOCCAPTURE:
+		{
+			int v, res;
+
+			if (copy_from_user(&v, arg, sizeof(v))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCCAPTURE: %d\n", zr->name,
+				v));
+
+			/* If there is nothing to do, return immediatly */
+
+			if ((v && zr->v4l_overlay_active)
+			    || (!v && !zr->v4l_overlay_active))
+				return 0;
+
+			if (v == 0) {
+				zr->v4l_overlay_active = 0;
+				if (!zr->v4l_memgrab_active)
+					zr36057_overlay(zr, 0);
+				/* When a grab is running, the video simply won't be switched on any more */
+			} else {
+				if (!zr->buffer_set || !zr->window_set) {
+					DEBUG1(printk(KERN_ERR
+					       "%s: VIDIOCCAPTURE: buffer or window not set\n",
+					       zr->name));
+					return -EINVAL;
+				}
+				zr->v4l_overlay_active = 1;
+				if (!zr->v4l_memgrab_active)
+					zr36057_overlay(zr, 1);
+				/* When a grab is running, the video will be switched on when grab is finished */
+			}
+			/* Make sure the changes come into effect */
+			res = wait_grab_pending(zr);
+			if (res)
+				return res;
+			return 0;
+		}
+		break;
+
+	case VIDIOCGWIN:
+		{
+			DEBUG2(printk("%s: ioctl VIDIOCGWIN\n", zr->name));
+			if (copy_to_user
+			    (arg, &zr->window, sizeof(zr->window))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case VIDIOCSWIN:
+		{
+			struct video_clip *vcp;
+			struct video_window vw;
+			struct tvnorm *tvn;
+			int on, end, res, Wa, Ha;
+
+			tvn = zr->timing;
+
+			Wa = tvn->Wa;
+			Ha = tvn->Ha;
+
+			if (copy_from_user(&vw, arg, sizeof(vw))) {
+				return -EFAULT;
+			}
+
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCSWIN: x=%d y=%d w=%d h=%d clipcount=%d\n",
+				zr->name, vw.x, vw.y, vw.width, vw.height,
+				vw.clipcount));
+
+			if (!zr->buffer_set) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSWIN: frame buffer has to be set first\n",
+				       zr->name));
+				return -EINVAL;
+			}
+
+			/*
+			 * The video front end needs 4-byte alinged line sizes, we correct that
+			 * silently here if necessary
+			 */
+
+			if (zr->buffer.depth == 15
+			    || zr->buffer.depth == 16) {
+				end = (vw.x + vw.width) & ~1;	/* round down */
+				vw.x = (vw.x + 1) & ~1;	/* round up */
+				vw.width = end - vw.x;
+			}
+
+			if (zr->buffer.depth == 24) {
+				end = (vw.x + vw.width) & ~3;	/* round down */
+				vw.x = (vw.x + 3) & ~3;	/* round up */
+				vw.width = end - vw.x;
+			}
+
+			if (vw.width > Wa)
+				vw.width = Wa;
+			if (vw.height > Ha)
+				vw.height = Ha;
+
+			/* Check for vaild parameters */
+			if (vw.width < BUZ_MIN_WIDTH
+			    || vw.height < BUZ_MIN_HEIGHT
+			    || vw.width > BUZ_MAX_WIDTH
+			    || vw.height > BUZ_MAX_HEIGHT) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSWIN: width = %d or height = %d invalid\n",
+				       zr->name, vw.width, vw.height));
+				return -EINVAL;
+			}
+
+			zr->window.x = vw.x;
+			zr->window.y = vw.y;
+			zr->window.width = vw.width;
+			zr->window.height = vw.height;
+			zr->window.chromakey = 0;
+			zr->window.flags = 0;	// RJ: Is this intended for interlace on/off ?
+			zr->window.clips = NULL;
+			zr->window.clipcount = vw.clipcount;
+
+			/*
+			 * If an overlay is running, we have to switch it off
+			 * and switch it on again in order to get the new settings in effect.
+			 *
+			 * We also want to avoid that the overlay mask is written
+			 * when an overlay is running.
+			 */
+
+			on = zr->v4l_overlay_active
+			    && !zr->v4l_memgrab_active;
+			if (on)
+				zr36057_overlay(zr, 0);
+
+			/*
+			 *   Write the overlay mask if clips are wanted.
+			 */
+			if (vw.clipcount) {
+				vcp =
+				    vmalloc(sizeof(struct video_clip) *
+					    (vw.clipcount + 4));
+				if (vcp == NULL) {
+					printk(KERN_ERR
+					       "%s: zoran_ioctl: Alloc of clip mask failed\n",
+					       zr->name);
+					return -ENOMEM;
+				}
+				if (copy_from_user
+				    (vcp, vw.clips,
+				     sizeof(struct video_clip) *
+				     vw.clipcount)) {
+					vfree(vcp);
+					return -EFAULT;
+				}
+				write_overlay_mask(zr, vcp, vw.clipcount);
+				vfree(vcp);
+			}
+
+			if (on)
+				zr36057_overlay(zr, 1);
+			zr->window_set = 1;
+
+			/* Make sure the changes come into effect */
+			res = wait_grab_pending(zr);
+			if (res)
+				return res;
+
+			return 0;
+		}
+		break;
+
+	case VIDIOCGFBUF:
+		{
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCGFBUF\n", zr->name));
+			if (copy_to_user
+			    (arg, &zr->buffer, sizeof(zr->buffer))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case VIDIOCSFBUF:
+		{
+			struct video_buffer v;
+
+			/* RJ: Isn't this too restrictive? As long as the user doesn't set
+			   the base address it shouldn't be too dangerous */
+
+			if (!capable(CAP_SYS_ADMIN)) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: Only the superuser may issue VIDIOCSFBUF ioctl\n",
+				       zr->name));
+				return -EPERM;
+			}
+			if (copy_from_user(&v, arg, sizeof(v))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCSFBUF: base=0x%x w=%d h=%d depth=%d bpl=%d\n",
+				zr->name, (u32) v.base, v.width, v.height,
+				v.depth, v.bytesperline));
+			if (zr->v4l_overlay_active) {
+				/* Has the user gotten crazy ... ? */
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSFBUF not allowed when overlay active\n",
+				       zr->name));
+				return -EINVAL;
+			}
+			if (v.depth != 15 && v.depth != 16 && v.depth != 24
+			    && v.depth != 32) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSFBUF: depth=%d not supported\n",
+				       zr->name, v.depth));
+				return -EINVAL;
+			}
+			if (v.height <= 0 || v.width <= 0
+			    || v.bytesperline <= 0) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSFBUF: invalid height/width/bpl value\n",
+				       zr->name));
+				return -EINVAL;
+			}
+			if (v.bytesperline & 3) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: VIDIOCSFBUF: bytesperline must be 4-byte aligned\n",
+				       zr->name));
+				return -EINVAL;
+			}
+			if (v.base) {
+				zr->buffer.base =
+				    (void *) ((unsigned long) v.base & ~3);
+			}
+			zr->buffer.height = v.height;
+			zr->buffer.width = v.width;
+			zr->buffer.depth = v.depth;
+			zr->buffer.bytesperline = v.bytesperline;
+
+			if (zr->buffer.base)
+				zr->buffer_set = 1;
+			zr->window_set = 0;	/* The user should set new window parameters */
+			return 0;
+		}
+		break;
+
+		/* RJ: what is VIDIOCKEY intended to do ??? */
+
+	case VIDIOCKEY:
+		{
+			/* Will be handled higher up .. */
+			DEBUG2(printk("%s: ioctl VIDIOCKEY\n", zr->name));
+			return 0;
+		}
+		break;
+
+	case VIDIOCGFREQ:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCGFREQ not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCSFREQ:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCSFREQ not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCGAUDIO:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCGAUDIO not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCSAUDIO:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCSAUDIO not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCSYNC:
+		{
+			int v;
+
+			if (copy_from_user(&v, arg, sizeof(v))) {
+				return -EFAULT;
+			}
+			DEBUG3(printk
+			       ("%s: ioctl VIDIOCSYNC %d\n", zr->name, v));
+			return v4l_sync(zr, v);
+		}
+		break;
+
+	case VIDIOCMCAPTURE:
+		{
+			struct video_mmap vm;
+
+			if (copy_from_user
+			    ((void *) &vm, (void *) arg, sizeof(vm))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCMCAPTURE frame=%d geom=%dx%d fmt=%d\n",
+				zr->name, vm.frame, vm.width, vm.height,
+				vm.format));
+			return v4l_grab(zr, &vm);
+		}
+		break;
+
+	case VIDIOCGMBUF:
+		{
+			struct video_mbuf vm;
+			int i;
+
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCGMBUF\n", zr->name));
+			vm.size = v4l_nbufs * v4l_bufsize;
+			vm.frames = v4l_nbufs;
+			for (i = 0; i < v4l_nbufs; i++) {
+				vm.offsets[i] = i * v4l_bufsize;
+			}
+
+			/* The next mmap will map the V4L buffers */
+			zr->map_mjpeg_buffers = 0;
+
+			if (copy_to_user(arg, &vm, sizeof(vm))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case VIDIOCGUNIT:
+		{
+			struct video_unit vu;
+
+			DEBUG2(printk
+			       ("%s: ioctl VIDIOCGUNIT\n", zr->name));
+			vu.video = zr->video_dev.minor;
+			vu.vbi = VIDEO_NO_UNIT;
+			vu.radio = VIDEO_NO_UNIT;
+			vu.audio = VIDEO_NO_UNIT;
+			vu.teletext = VIDEO_NO_UNIT;
+			if (copy_to_user(arg, &vu, sizeof(vu))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+		/*
+		 * RJ: In principal we could support subcaptures for V4L grabbing.
+		 *     Not even the famous BTTV driver has them, however.
+		 *     If there should be a strong demand, one could consider
+		 *     to implement them.
+		 */
+	case VIDIOCGCAPTURE:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCGCAPTURE not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case VIDIOCSCAPTURE:
+		{
+			DEBUG1(printk(KERN_ERR
+			       "%s: ioctl VIDIOCSCAPTURE not supported\n",
+			       zr->name));
+			return -EINVAL;
+		}
+		break;
+
+	case BUZIOC_G_PARAMS:
+		{
+			DEBUG2(printk
+			       ("%s: ioctl BUZIOC_G_PARAMS\n", zr->name));
+
+			if (copy_to_user
+			    (arg, &(zr->params), sizeof(zr->params))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case BUZIOC_S_PARAMS:
+		{
+			struct zoran_params bp;
+			/* int input, on; */
+
+			if (zr->codec_mode != BUZ_MODE_IDLE) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: BUZIOC_S_PARAMS called but Buz in capture/playback mode\n",
+				       zr->name));
+				return -EINVAL;
+			}
+
+			if (copy_from_user(&bp, arg, sizeof(bp))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl BUZIOC_S_PARAMS\n", zr->name));
+
+			/* Check the params first before overwriting our internal values */
+
+			if (zoran_check_params(zr, &bp))
+				return -EINVAL;
+
+			zr->params = bp;
+
+			/* Make changes of input and norm go into effect immediatly */
+
+			/* We switch overlay off and on since a change in the norm
+			   needs different VFE settings */
+
+			if (copy_to_user(arg, &bp, sizeof(bp))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case BUZIOC_REQBUFS:
+		{
+			struct zoran_requestbuffers br;
+
+			if (zr->jpg_buffers_allocated) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: BUZIOC_REQBUFS: buffers allready allocated\n",
+				       zr->name));
+				return -EINVAL;
+			}
+			if (copy_from_user(&br, arg, sizeof(br))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl BUZIOC_REQBUFS count = %lu size=%lu\n",
+				zr->name, br.count, br.size));
+
+			/* Enforce reasonable lower and upper limits */
+			if (br.count < 4)
+				br.count = 4;	/* Could be choosen smaller */
+			if (br.count > BUZ_MAX_FRAME)
+				br.count = BUZ_MAX_FRAME;
+			br.size = PAGE_ALIGN(br.size);
+			if (br.size < 8192)
+				br.size = 8192;	/* Arbitrary */
+			/* br.size is limited by 1 page for the stat_com tables to a Maximum of 2 MB */
+			if (br.size > (512 * 1024))
+				br.size = (512 * 1024);	/* 512 K should be enough */
+			if (zr->need_contiguous
+			    && br.size > MAX_KMALLOC_MEM)
+				br.size = MAX_KMALLOC_MEM;
+
+			zr->jpg_nbufs = br.count;
+			zr->jpg_bufsize = br.size;
+
+			if (jpg_fbuffer_alloc(zr))
+				return -ENOMEM;
+
+			/* The next mmap will map the MJPEG buffers */
+			zr->map_mjpeg_buffers = 1;
+
+			if (copy_to_user(arg, &br, sizeof(br))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	case BUZIOC_QBUF_CAPT:
+		{
+			int nb;
+
+			if (copy_from_user
+			    ((void *) &nb, (void *) arg, sizeof(int))) {
+				return -EFAULT;
+			}
+			DEBUG4(printk
+			       ("%s: ioctl BUZIOC_QBUF_CAPT %d\n",
+				zr->name, nb));
+			return jpg_qbuf(zr, nb, BUZ_MODE_MOTION_COMPRESS);
+		}
+		break;
+
+	case BUZIOC_QBUF_PLAY:
+		{
+			int nb;
+
+			if (copy_from_user
+			    ((void *) &nb, (void *) arg, sizeof(int))) {
+				return -EFAULT;
+			}
+			DEBUG4(printk
+			       ("%s: ioctl BUZIOC_QBUF_PLAY %d\n",
+				zr->name, nb));
+			return jpg_qbuf(zr, nb,
+					BUZ_MODE_MOTION_DECOMPRESS);
+		}
+		break;
+
+	case BUZIOC_SYNC:
+		{
+			struct zoran_sync bs;
+			int res;
+
+			DEBUG4(printk
+			       ("%s: ioctl BUZIOC_SYNC\n", zr->name));
+			res = jpg_sync(zr, &bs);
+			if (copy_to_user(arg, &bs, sizeof(bs))) {
+				return -EFAULT;
+			}
+			return res;
+		}
+		break;
+
+	case BUZIOC_G_STATUS:
+		{
+			struct zoran_status bs;
+			int norm, input, status;
+			unsigned long timeout;
+
+			if (zr->codec_mode != BUZ_MODE_IDLE) {
+				DEBUG1(printk(KERN_ERR
+				       "%s: BUZIOC_G_STATUS called but Buz in capture/playback mode\n",
+				       zr->name));
+				return -EINVAL;
+			}
+
+			if (copy_from_user(&bs, arg, sizeof(bs))) {
+				return -EFAULT;
+			}
+			DEBUG2(printk
+			       ("%s: ioctl BUZIOC_G_STATUS\n", zr->name));
+
+			switch (bs.input) {
+			case 0:
+				if (zr->card == BUZ)
+					input = 3;
+				else
+					input = 0;
+				break;
+			case 1:
+				input = 7;
+				break;
+			default:
+				DEBUG1(printk(KERN_ERR
+				       "%s: BUZIOC_G_STATUS on not existing input %d\n",
+				       zr->name, bs.input));
+				return -EINVAL;
+			}
+
+			/* Set video norm to VIDEO_MODE_AUTO */
+
+			norm = VIDEO_MODE_AUTO;
+			set_videobus_enable(zr, 0);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_INPUT, &input);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_NORM, &norm);
+			set_videobus_enable(zr, 1);
+
+			/* sleep 1 second */
+
+			timeout = jiffies + 1 * HZ;
+			while (jiffies < timeout)
+				schedule();
+
+			/* Get status of video decoder */
+
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_GET_STATUS, &status);
+			bs.signal = (status & DECODER_STATUS_GOOD) ? 1 : 0;
+
+			if (status & DECODER_STATUS_NTSC)
+				bs.norm = VIDEO_MODE_NTSC;
+			else if (status & DECODER_STATUS_SECAM)
+				bs.norm = VIDEO_MODE_SECAM;
+			else
+				bs.norm = VIDEO_MODE_PAL;
+
+			bs.color = (status & DECODER_STATUS_COLOR) ? 1 : 0;
+
+			/* restore previous input and norm */
+			if (zr->card == BUZ)
+				input = zr->params.input == 0 ? 3 : 7;
+			else
+				input = zr->params.input == 0 ? 0 : 7;
+			set_videobus_enable(zr, 0);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_INPUT, &input);
+			i2c_control_device(&zr->i2c,
+					   I2C_DRIVERID_VIDEODECODER,
+					   DECODER_SET_NORM,
+					   &zr->params.norm);
+			set_videobus_enable(zr, 1);
+
+			if (copy_to_user(arg, &bs, sizeof(bs))) {
+				return -EFAULT;
+			}
+			return 0;
+		}
+		break;
+
+	default:
+		DEBUG1(printk
+		       ("%s: UNKNOWN ioctl cmd: 0x%x\n", zr->name, cmd));
+		return -ENOIOCTLCMD;
+	}
+	return 0;
+}
+
+static int zoran_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+	struct zoran *zr = (struct zoran *) dev;
+	int err;
+	
+	down(&zr->sem);
+	err = do_zoran_ioctl(zr, cmd, arg);
+	up(&zr->sem);
+	
+	return err;
+}
+
+/*
+ *   This maps the buffers to user space.
+ *
+ *   Depending on the state of zr->map_mjpeg_buffers
+ *   the V4L or the MJPEG buffers are mapped
+ *
+ */
+
+static int do_zoran_mmap(struct zoran *zr, const char *adr,
+		      unsigned long size)
+{
+	unsigned long start = (unsigned long) adr;
+	unsigned long page, pos, todo, fraglen;
+	int i, j;
+
+	DEBUG2(printk
+	       (KERN_INFO "%s: mmap at 0x%08lx, size %lu\n", zr->name,
+		start, size));
+	if (zr->map_mjpeg_buffers) {
+		/* Map the MJPEG buffers */
+
+		if (!zr->jpg_buffers_allocated) {
+			DEBUG1(printk(KERN_ERR
+			       "%s: zoran_mmap(MJPEG): buffers not yet allocated\n",
+			       zr->name));
+			return -ENOMEM;
+		}
+
+		if (size > zr->jpg_nbufs * zr->jpg_bufsize) {
+			DEBUG1(printk(KERN_ERR
+			       "%s: zoran_mmap(MJPEG): Max size is %lu - you wanted %lu\n",
+			       zr->name, zr->jpg_nbufs * zr->jpg_bufsize,
+			       size));
+			return -EINVAL;
+		}
+
+		if (size != zr->jpg_nbufs * zr->jpg_bufsize)
+			DEBUG1(printk(KERN_WARNING
+			       "%s: zoran_mmap(MJPEG): Expected %lu - you wanted %lu\n",
+			       zr->name, zr->jpg_nbufs * zr->jpg_bufsize,
+			       size));
+
+		for (i = 0; i < zr->jpg_nbufs; i++) {
+			for (j = 0; j < zr->jpg_bufsize / PAGE_SIZE; j++) {
+				fraglen =
+				    (zr->jpg_gbuf[i].
+				     frag_tab[2 * j + 1] & ~1) << 1;
+				todo = size;
+				if (todo > fraglen)
+					todo = fraglen;
+				pos =
+				    (unsigned long) zr->jpg_gbuf[i].
+				    frag_tab[2 * j];
+				page = virt_to_phys(bus_to_virt(pos));	/* should just be pos on i386 */
+				if (remap_page_range
+				    (start, page, todo, PAGE_SHARED)) {
+					printk(KERN_ERR
+					       "%s: zoran_mmap(V4L): remap_page_range failed\n",
+					       zr->name);
+					return -EAGAIN;
+				}
+				size -= todo;
+				start += todo;
+				if (size == 0)
+					break;
+				if (zr->jpg_gbuf[i].
+				    frag_tab[2 * j + 1] & 1)
+					break;	/* was last fragment */
+			}
+			if (size == 0)
+				break;
+		}
+	} else {
+		/* Map the V4L buffers */
+
+		if (size > v4l_nbufs * v4l_bufsize) {
+			DEBUG1(printk(KERN_ERR
+			       "%s: zoran_mmap(V4L): Max size is %d - you wanted %ld\n",
+			       zr->name, v4l_nbufs * v4l_bufsize, size));
+			return -EINVAL;
+		}
+
+		if (size != v4l_nbufs * v4l_bufsize)
+			DEBUG1(printk(KERN_WARNING
+			       "%s: zoran_mmap(V4L): Expected %d - you wanted %ld\n",
+			       zr->name, v4l_nbufs * v4l_bufsize, size));
+
+		for (i = 0; i < v4l_nbufs; i++) {
+			todo = size;
+			if (todo > v4l_bufsize)
+				todo = v4l_bufsize;
+			page = zr->v4l_gbuf[i].fbuffer_phys;
+			DEBUG2(printk
+			       ("V4L remap page range %d 0x%lx %ld to 0x%lx\n",
+				i, page, todo, start));
+			if (remap_page_range
+			    (start, page, todo, PAGE_SHARED)) {
+				printk(KERN_ERR
+				       "%s: zoran_mmap(V4L): remap_page_range failed\n",
+				       zr->name);
+				return -EAGAIN;
+			}
+			size -= todo;
+			start += todo;
+			if (size == 0)
+				break;
+		}
+	}
+	return 0;
+}
+
+static int zoran_mmap(struct video_device *dev, const char *adr,
+		      unsigned long size)
+{
+	int err;
+	struct zoran *zr = (struct zoran *) dev;
+	
+	down(&zr->sem);
+	err = do_zoran_mmap(zr, adr, size);
+	up(&zr->sem);
+		
+	return err;
+}
+
+static int zoran_init_done(struct video_device *dev)
+{
+	return 0;
+}
+
+static struct video_device zoran_template = {
+	THIS_MODULE,
+	ZORAN_NAME,
+	VID_TYPE_CAPTURE | VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
+	    VID_TYPE_FRAMERAM | VID_TYPE_SCALES | VID_TYPE_SUBCAPTURE,
+	ZORAN_HARDWARE,
+	zoran_open,
+	zoran_close,
+	zoran_read,
+	zoran_write,
+	NULL,
+	zoran_ioctl,
+	zoran_mmap,
+	zoran_init_done,
+	NULL,
+	0,
+	0
+};
+
+/*
+ * initialize video front end
+ */
+static void zr36057_init_vfe(struct zoran *zr)
+{
+	u32 reg;
+	reg = btread(ZR36057_VFESPFR);
+	reg |= ZR36057_VFESPFR_LittleEndian;
+	reg &= ~ZR36057_VFESPFR_VCLKPol;
+	reg |= ZR36057_VFESPFR_ExtFl;
+	reg |= ZR36057_VFESPFR_TopField;
+	btwrite(reg, ZR36057_VFESPFR);
+	reg = btread(ZR36057_VDCR);
+	if (triton || zr->revision <= 1)
+		reg &= ~ZR36057_VDCR_Triton;
+	else
+		reg |= ZR36057_VDCR_Triton;
+	btwrite(reg, ZR36057_VDCR);
+}
+
+static void test_interrupts(struct zoran *zr)
+{
+	int timeout, icr;
+
+	clear_interrupt_counters(zr);
+	zr->testing = 1;
+	icr = btread(ZR36057_ICR);
+	btwrite(0x78000000 | ZR36057_ICR_IntPinEn, ZR36057_ICR);
+	timeout = interruptible_sleep_on_timeout(&zr->test_q, 1 * HZ);
+	btwrite(0, ZR36057_ICR);
+	btwrite(0x78000000, ZR36057_ISR);
+	zr->testing = 0;
+	printk(KERN_INFO "%s: Testing interrupts...\n", zr->name);
+	if (timeout) {
+		printk(": time spent: %d\n", 1 * HZ - timeout);
+	}
+	print_interrupts(zr);
+	btwrite(icr, ZR36057_ICR);
+}
+
+static int zr36057_init(int i)
+{
+	struct zoran *zr = &zoran[i];
+	unsigned long mem;
+	unsigned mem_needed;
+	int j;
+	int two = 2;
+	int zero = 0;
+
+	printk(KERN_INFO "%s: Initializing card[%d], zr=%x\n", zr->name, i, (int) zr);
+
+	/* default setup of all parameters which will persist beetween opens */
+
+	zr->user = 0;
+
+	init_waitqueue_head(&zr->v4l_capq);
+	init_waitqueue_head(&zr->jpg_capq);
+	init_waitqueue_head(&zr->test_q);
+
+	zr->map_mjpeg_buffers = 0;	/* Map V4L buffers by default */
+
+	zr->jpg_nbufs = 0;
+	zr->jpg_bufsize = 0;
+	zr->jpg_buffers_allocated = 0;
+
+	zr->buffer_set = 0;	/* Flag if frame buffer has been set */
+	zr->buffer.base = (void *) vidmem;
+	zr->buffer.width = 0;
+	zr->buffer.height = 0;
+	zr->buffer.depth = 0;
+	zr->buffer.bytesperline = 0;
+
+	zr->params.norm = default_norm = (default_norm < 3 ? default_norm : VIDEO_MODE_PAL);	/* Avoid nonsense settings from user */
+	if (!(zr->timing = cardnorms[zr->card][zr->params.norm])) {
+		printk(KERN_WARNING
+		       "%s: default TV statdard not supported by hardware. PAL will be used.\n",
+		       zr->name);
+		zr->params.norm = VIDEO_MODE_PAL;
+		zr->timing = cardnorms[zr->card][zr->params.norm];
+	}
+	zr->params.input = default_input = (default_input ? 1 : 0);	/* Avoid nonsense settings from user */
+	zr->video_interlace = 0;
+
+	/* Should the following be reset at every open ? */
+
+	zr->picture.colour = 32768;
+	zr->picture.brightness = 32768;
+	zr->picture.hue = 32768;
+	zr->picture.contrast = 32768;
+	zr->picture.whiteness = 0;
+	zr->picture.depth = 0;
+	zr->picture.palette = 0;
+
+	for (j = 0; j < VIDEO_MAX_FRAME; j++) {
+		zr->v4l_gbuf[i].fbuffer = 0;
+		zr->v4l_gbuf[i].fbuffer_phys = 0;
+		zr->v4l_gbuf[i].fbuffer_bus = 0;
+	}
+
+	zr->stat_com = 0;
+
+	/* default setup (will be repeated at every open) */
+
+	zoran_open_init_params(zr);
+
+	/* allocate memory *before* doing anything to the hardware in case allocation fails */
+
+	/* STAT_COM table and overlay mask */
+
+	mem_needed = (BUZ_NUM_STAT_COM + ((BUZ_MAX_WIDTH + 31) / 32) * BUZ_MAX_HEIGHT) * 4;
+	mem = (unsigned long) kmalloc(mem_needed, GFP_KERNEL);
+	if (!mem) {
+		printk(KERN_ERR "%s: zr36057_init: kmalloc (STAT_COM + ovl.mask) failed\n", zr->name);
+		return -ENOMEM;
+	}
+	memset((void *) mem, 0, mem_needed);
+
+	zr->stat_com = (u32 *) mem;
+	for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+		zr->stat_com[j] = 1;	/* mark as unavailable to zr36057 */
+	}
+	zr->overlay_mask = (u32 *) (mem + BUZ_NUM_STAT_COM * 4);
+
+	/* Initialize zr->jpg_gbuf */
+
+	for (j = 0; j < BUZ_MAX_FRAME; j++) {
+		zr->jpg_gbuf[j].frag_tab = 0;
+		zr->jpg_gbuf[j].frag_tab_bus = 0;
+		zr->jpg_gbuf[j].state = BUZ_STATE_USER;
+		zr->jpg_gbuf[j].bs.frame = j;
+	}
+
+	/*
+	 *   Now add the template and register the device unit.
+	 */
+	memcpy(&zr->video_dev, &zoran_template, sizeof(zoran_template));
+	strcpy(zr->video_dev.name, zr->name);
+	if (video_register_device(&zr->video_dev, VFL_TYPE_GRABBER, video_nr) < 0) {
+		i2c_unregister_bus(&zr->i2c);
+		kfree((void *) zr->stat_com);
+		return -1;
+	}
+
+	/* Enable bus-mastering */
+	pci_set_master(zr->pci_dev);
+
+	if (zr->card == BUZ)
+		j = zr->params.input == 0 ? 3 : 7;
+	else
+		j = zr->params.input == 0 ? 0 : 7;
+	set_videobus_enable(zr, 0);
+	i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+			   DECODER_SET_INPUT, &j);
+	i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+			   DECODER_SET_NORM, &zr->params.norm);
+	i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+			   ENCODER_SET_NORM, &zr->params.norm);
+	set_videobus_enable(zr, 1);
+
+	/* toggle JPEG codec sleep to sync PLL */
+	zr36060_sleep(zr, 1);
+	zr36060_sleep(zr, 0);
+
+	/* set individual interrupt enables (without GIRQ1)
+	   but don't global enable until zoran_open() */
+
+	//btwrite(IRQ_MASK & ~ZR36057_ISR_GIRQ1, ZR36057_ICR);  // SW
+	// It looks like using only JPEGRepIRQEn is not always reliable,
+	// may be when JPEG codec crashes it won't generate IRQ? So,
+	btwrite(IRQ_MASK, ZR36057_ICR);	// Enable Vsync interrupts too. SM
+
+	zr36057_init_vfe(zr);
+
+	zr->zoran_proc = NULL;
+	zr->initialized = 1;
+
+	zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+#if (DEBUGLEVEL > 2)
+	detect_guest_activity(zr);
+#endif
+	test_interrupts(zr);
+	btwrite(IRQ_MASK, ZR36057_ICR);	// Enable Vsync interrupts too. SM
+	if (!pass_through) {
+		set_videobus_enable(zr, 0);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEODECODER,
+				   DECODER_ENABLE_OUTPUT, &zero);
+		i2c_control_device(&zr->i2c, I2C_DRIVERID_VIDEOENCODER,
+				   ENCODER_SET_INPUT, &two);
+		set_videobus_enable(zr, 1);
+	}
+	return 0;
+}
+
+#include "zoran_procfs.c"
+
+static void release_dc10(void)
+{
+	u8 command;
+	int i;
+	struct zoran *zr;
+
+	for (i = 0; i < zoran_num; i++) {
+		zr = &zoran[i];
+
+		if (!zr->initialized)
+			continue;
+
+		/* unregister i2c_bus */
+		i2c_unregister_bus((&zr->i2c));
+
+		/* disable PCI bus-mastering */
+		pci_read_config_byte(zr->pci_dev, PCI_COMMAND, &command);
+		command &= ~PCI_COMMAND_MASTER;
+		pci_write_config_byte(zr->pci_dev, PCI_COMMAND, command);
+
+		/* put chip into reset */
+		btwrite(0, ZR36057_SPGPPCR);
+
+		free_irq(zr->pci_dev->irq, zr);
+
+		/* unmap and free memory */
+
+		kfree((void *) zr->stat_com);
+
+		zoran_proc_cleanup(i);
+		iounmap(zr->zr36057_mem);
+
+		video_unregister_device(&zr->video_dev);
+	}
+}
+
+/*
+ *   Scan for a Buz card (actually for the PCI contoler ZR36057),
+ *   request the irq and map the io memory
+ */
+
+static int find_zr36057(void)
+{
+	unsigned char latency, need_latency;
+	struct zoran *zr;
+	struct pci_dev *dev = NULL;
+	int result;
+
+	zoran_num = 0;
+
+	while (zoran_num < BUZ_MAX
+	       && (dev =
+		   pci_find_device(PCI_VENDOR_ID_ZORAN,
+				   PCI_DEVICE_ID_ZORAN_36057,
+				   dev)) != NULL) {
+		zr = &zoran[zoran_num];
+		zr->pci_dev = dev;
+		zr->zr36057_mem = NULL;
+		zr->id = zoran_num;
+		sprintf(zr->name, "MJPEG[%u]", zr->id);
+
+		spin_lock_init(&zr->lock);
+		init_MUTEX(&zr->sem);
+
+		if (pci_enable_device(dev))
+			continue;
+
+		zr->zr36057_adr = pci_resource_start(zr->pci_dev, 0);
+		pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION,
+				     &zr->revision);
+		if (zr->revision < 2) {
+			printk(KERN_INFO
+			       "%s: Zoran ZR36057 (rev %d) irq: %d, memory: 0x%08x.\n",
+			       zr->name, zr->revision, zr->pci_dev->irq,
+			       zr->zr36057_adr);
+		} else {
+			unsigned short ss_vendor_id, ss_id;
+			ss_vendor_id = zr->pci_dev->subsystem_vendor;
+			ss_id = zr->pci_dev->subsystem_device;
+			printk(KERN_INFO
+			       "%s: Zoran ZR36067 (rev %d) irq: %d, memory: 0x%08x\n",
+			       zr->name, zr->revision, zr->pci_dev->irq,
+			       zr->zr36057_adr);
+			printk(KERN_INFO
+			       "%s: subsystem vendor=0x%04x id=0x%04x\n",
+			       zr->name, ss_vendor_id, ss_id);
+		}
+
+		zr->zr36057_mem = ioremap_nocache(zr->zr36057_adr, 0x1000);
+		if (!zr->zr36057_mem) {
+			printk(KERN_ERR "%s: ioremap failed\n", zr->name);
+			/* XXX handle error */
+		}
+
+		result = request_irq(zr->pci_dev->irq, zoran_irq, SA_SHIRQ | SA_INTERRUPT, zr->name, (void *) zr);
+		if (result < 0) {
+			if (result == -EINVAL) {
+				printk(KERN_ERR
+				       "%s: Bad irq number or handler\n",
+				       zr->name);
+			} else if (result == -EBUSY) {
+				printk(KERN_ERR
+				       "%s: IRQ %d busy, change your PnP config in BIOS\n",
+				       zr->name, zr->pci_dev->irq);
+			} else {
+				printk(KERN_ERR
+				       "%s: Can't assign irq, error code %d\n",
+				       zr->name, result);
+			}
+			iounmap(zr->zr36057_mem);
+			continue;
+		}
+
+		/* set PCI latency timer */
+		pci_read_config_byte(zr->pci_dev, PCI_LATENCY_TIMER, &latency);
+		need_latency = zr->revision > 1 ? 32 : 48;
+		if (latency != need_latency) {
+			printk(KERN_INFO "%s: Changing PCI latency from %d to %d.\n",  zr->name, latency, need_latency);
+			pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,  need_latency);
+		}
+
+		btwrite(0, ZR36057_SPGPPCR);
+		mdelay(1);
+		btwrite(ZR36057_SPGPPCR_SoftReset, ZR36057_SPGPPCR);
+		mdelay(1);
+
+		/* assert P_Reset */
+		btwrite(0, ZR36057_JPC);
+
+		/* set up GPIO direction - all output */
+		btwrite(ZR36057_SPGPPCR_SoftReset | 0, ZR36057_SPGPPCR);
+		btwrite((0x81 << 24) | 0x8888, ZR36057_GPPGCR1);
+
+		/* i2c */
+		memcpy(&zr->i2c, &zoran_i2c_bus_template,
+		       sizeof(struct i2c_bus));
+		strcpy(zr->i2c.name, zr->name);
+		zr->i2c.data = zr;
+		printk(KERN_INFO "%s: Initializing i2c bus...\n", zr->name);
+		if (i2c_register_bus(&zr->i2c) < 0) {
+			/* put chip into reset */
+			btwrite(0, ZR36057_SPGPPCR);
+			free_irq(zr->pci_dev->irq, zr);
+			iounmap(zr->zr36057_mem);
+			printk(KERN_ERR "%s: Can't initialize i2c bus\n", zr->name);
+			continue;
+		}
+
+		if (zr->card != DC10 && zr->card != DC10plus
+		    && zr->card != LML33 && zr->card != BUZ) {
+			/* unregister i2c_bus */
+			i2c_unregister_bus((&zr->i2c));
+			/* put chip into reset */
+			btwrite(0, ZR36057_SPGPPCR);
+			free_irq(zr->pci_dev->irq, zr);
+			iounmap(zr->zr36057_mem);
+			printk(KERN_ERR "%s: Card not supported\n",
+			       zr->name);
+			continue;
+		}
+		printk(KERN_INFO "%s card detected\n", zr->name);
+		if (zr->card == LML33) {
+			GPIO(zr, 2, 1);	// Set Composite input/output
+		}
+
+		/* reset JPEG codec */
+		zr36060_sleep(zr, 1);
+		zr36060_reset(zr);
+
+		/* video bus enabled */
+
+		/* display codec revision */
+		if (zr36060_read_8(zr, 0x022) == 0x33) {
+			printk(KERN_INFO "%s: Zoran ZR36060 (rev %d)\n",
+			       zr->name, zr36060_read_8(zr, 0x023));
+		} else {
+			/* unregister i2c_bus */
+			i2c_unregister_bus((&zr->i2c));
+			/* put chip into reset */
+			btwrite(0, ZR36057_SPGPPCR);
+			free_irq(zr->pci_dev->irq, zr);
+			iounmap(zr->zr36057_mem);
+			printk(KERN_ERR "%s: Zoran ZR36060 not found\n",
+			       zr->name);
+			continue;
+		}
+
+		zoran_num++;
+	}
+	if (zoran_num == 0) {
+		printk(KERN_INFO "No known MJPEG cards found.\n");
+	}
+	return zoran_num;
+}
+
+static void handle_chipset(void)
+{
+	if(pci_pci_problems & PCIPCI_FAIL)
+		printk(KERN_WARNING "Chipset may not support reliable PCI-PCI DMA.\n");
+
+	if(pci_pci_problems & PCIPCI_TRITON)
+	{
+		printk(KERN_WARNING "Enabling Triton support.\n");
+		triton = 1;
+	}
+	
+	if(pci_pci_problems & PCIPCI_NATOMA)
+	{
+		printk(KERN_WARNING "Enabling Natoma workaround.\n");
+		natoma = 1;
+	}
+}
+
+static int init_dc10_cards(void)
+{
+	int i;
+
+	memset(zoran, 0, sizeof(zoran));
+	printk(KERN_INFO
+	       "Zoran ZR36060 + ZR36057/67 MJPEG board driver version %d.%d\n",
+	       MAJOR_VERSION, MINOR_VERSION);
+
+	/* Look for cards */
+
+	if (find_zr36057() < 0) {
+		return -EIO;
+	}
+	if (zoran_num == 0)
+		return 0;	//-ENXIO;
+
+	printk(KERN_INFO "MJPEG: %d card(s) found\n", zoran_num);
+
+	/* check the parameters we have been given, adjust if necessary */
+
+	if (v4l_nbufs < 0)
+		v4l_nbufs = 0;
+	if (v4l_nbufs > VIDEO_MAX_FRAME)
+		v4l_nbufs = VIDEO_MAX_FRAME;
+	/* The user specfies the in KB, we want them in byte (and page aligned) */
+	v4l_bufsize = PAGE_ALIGN(v4l_bufsize * 1024);
+	if (v4l_bufsize < 32768)
+		v4l_bufsize = 32768;
+	/* 2 MB is arbitrary but sufficient for the maximum possible images */
+	if (v4l_bufsize > 2048 * 1024)
+		v4l_bufsize = 2048 * 1024;
+
+	printk(KERN_INFO "MJPEG: using %d V4L buffers of size %d KB\n",
+	       v4l_nbufs, v4l_bufsize >> 10);
+
+	/* Use parameter for vidmem or try to find a video card */
+
+	if (vidmem) {
+		printk(KERN_INFO
+		       "MJPEG: Using supplied video memory base address @ 0x%lx\n",
+		       vidmem);
+	}
+	/* check if we have a Triton or Natome chipset */
+
+	handle_chipset();
+
+	/* take care of Natoma chipset and a revision 1 zr36057 */
+
+	for (i = 0; i < zoran_num; i++) {
+		if (natoma && zoran[i].revision <= 1) {
+			zoran[i].need_contiguous = 1;
+			printk(KERN_INFO
+			       "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+			       zoran[i].name);
+		} else {
+			zoran[i].need_contiguous = 0;
+		}
+	}
+
+	/* initialize the Buzs */
+
+	/* We have to know which ones must be released if an error occurs */
+	for (i = 0; i < zoran_num; i++)
+		zoran[i].initialized = 0;
+
+	for (i = 0; i < zoran_num; i++) {
+		if (zr36057_init(i) < 0) {
+			release_dc10();
+			return -EIO;
+		}
+		zoran_proc_init(i);
+	}
+
+	return 0;
+}
+
+static void unload_dc10_cards(void)
+{
+	release_dc10();
+}
+
+
+module_init(init_dc10_cards);
+module_exit(unload_dc10_cards);

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