#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/slab.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 <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");

static struct pci_device_id zr36067_pci_tbl[] = {
	{ PCI_VENDOR_ID_ZORAN, PCI_DEVICE_ID_ZORAN_36057, 
	  PCI_ANY_ID, PCI_ANY_ID,  0, 0, 0 },
	{ 0 }
};
MODULE_DEVICE_TABLE(pci, zr36067_pci_tbl);

/* 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];
		do_gettimeofday(&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;

			/*
			 * 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 > 2048)
				return -EINVAL;
			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);
			}
			zr->window.clipcount = vw.clipcount;
	
			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 vm_area_struct *vma, 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
				    (vma, 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
			    (vma, 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 vm_area_struct *vma, 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(vma, 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);
MODULE_LICENSE("GPL");
