Re: PCI DMA kernel driver patch

Klaus Stengel <[email protected]> Thu, 22 May 2008 17:03:56 +0200
Newsgroups gmane.comp.video.mjpeg.devel
Message-ID <[email protected]>
Hi,

Am Donnerstag, den 22.05.2008, 06:41 +0200 schrieb Bernhard Praschinger:
> > You can find the necessary Linux kernel patch in the attachment. It's
> > against 2.6.24, but should apply to newer versions without any major
> > problems. Please review and maybe also test on big-endian machines, as
> > I'm not sure if I put all calls to cpu_to_le32() in the right places.
> Do you think the patch also works with older kernels like a 2.6.20 I use ?
> 
> If not I will have to compile a new kernel ( for example the 2.6.25.4) 
> and test it on a 32 Bit machine.

Support for BIGPHYS_AREA and HIMEM memory allocation was removed
somewhere between 2.6.21 and 2.6.24, so the patch probably won't work
with older versions. The patch in the attachment works with kernel
versions 2.6.18 to 2.6.20 and should issue a warning in case one of the
alternate allocation methods is tried. 

Regards,
Klaus.

-------------------------------------------------------------------------
This SF.net email is sponsored by: Microsoft
Defy all challenges. Microsoft(R) Visual Studio 2008.
http://clk.atdmt.com/MRT/go/vse0120000070mrt/direct/01/

_______________________________________________
Mjpeg-developer mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/mjpeg-developer
zoran_dma_2.6.18.diff (text/x-patch, 23 KB)
diff -Naur linux-2.6.18_orig/drivers/media/video/zoran_card.c linux-2.6.18/drivers/media/video/zoran_card.c
--- linux-2.6.18_orig/drivers/media/video/zoran_card.c	2008-05-22 16:57:47.000000000 +0200
+++ linux-2.6.18/drivers/media/video/zoran_card.c	2008-05-22 16:58:44.000000000 +0200
@@ -1092,6 +1092,14 @@
 		"%s: zr36057_init() - initializing card[%d], zr=%p\n",
 		ZR_DEVNAME(zr), zr->id, zr);
 
+	if (pci_set_dma_mask(zr->pci_dev, DMA_32BIT_MASK)) {
+		dprintk(1,
+			KERN_ERR
+			"%s: zr36057_init() - failed to set DMA mask to 32 bits.\n",
+			ZR_DEVNAME(zr));
+		return -EIO;
+	}
+
 	/* default setup of all parameters which will persist between opens */
 	zr->user = 0;
 
@@ -1134,7 +1142,8 @@
 
 	/* allocate memory *before* doing anything to the hardware
 	 * in case allocation fails */
-	zr->stat_com = kzalloc(BUZ_NUM_STAT_COM * 4, GFP_KERNEL);
+	zr->stat_com = pci_alloc_consistent(zr->pci_dev, 
+		BUZ_NUM_STAT_COM * 4, &zr->stat_com_bus);
 	zr->video_dev = kmalloc(sizeof(struct video_device), GFP_KERNEL);
 	if (!zr->stat_com || !zr->video_dev) {
 		dprintk(1,
@@ -1145,7 +1154,7 @@
 		goto exit_free;
 	}
 	for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
-		zr->stat_com[j] = 1;	/* mark as unavailable to zr36057 */
+		zr->stat_com[j] = cpu_to_le32(1);  /* mark as unavailable to zr36057 */
 	}
 
 	/*
@@ -1173,7 +1182,8 @@
 exit_unregister:
 	zoran_unregister_i2c(zr);
 exit_free:
-	kfree(zr->stat_com);
+	pci_free_consistent(zr->pci_dev, BUZ_NUM_STAT_COM * 4, zr->stat_com,
+		zr->stat_com_bus);
 	kfree(zr->video_dev);
 	return err;
 }
@@ -1205,7 +1215,8 @@
 	btwrite(0, ZR36057_SPGPPCR);
 	free_irq(zr->pci_dev->irq, zr);
 	/* unmap and free memory */
-	kfree(zr->stat_com);
+	pci_free_consistent(zr->pci_dev, BUZ_NUM_STAT_COM * 4, zr->stat_com,
+		zr->stat_com_bus);
 	zoran_proc_cleanup(zr);
 	iounmap(zr->zr36057_mem);
 	pci_disable_device(zr->pci_dev);
diff -Naur linux-2.6.18_orig/drivers/media/video/zoran_device.c linux-2.6.18/drivers/media/video/zoran_device.c
--- linux-2.6.18_orig/drivers/media/video/zoran_device.c	2008-05-22 16:57:47.000000000 +0200
+++ linux-2.6.18/drivers/media/video/zoran_device.c	2008-05-22 16:58:44.000000000 +0200
@@ -496,9 +496,9 @@
 		 * zr->overlay_settings.width instead of video_width */
 
 		mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
-		reg = virt_to_bus(zr->overlay_mask);
+		reg = cpu_to_le32(zr->overlay_mask_bus);
 		btwrite(reg, ZR36057_MMTR);
-		reg = virt_to_bus(zr->overlay_mask + mask_line_size);
+		reg = cpu_to_le32(zr->overlay_mask_bus + mask_line_size);
 		btwrite(reg, ZR36057_MMBR);
 		reg =
 		    mask_line_size - (zr->overlay_settings.width +
@@ -506,7 +506,7 @@
 		if (DispMode == 0)
 			reg += mask_line_size;
 		reg <<= ZR36057_OCR_MaskStride;
-		btwrite(reg, ZR36057_OCR);
+		btwrite(cpu_to_le32(reg), ZR36057_OCR);
 	}
 
 	zr36057_adjust_vfe(zr, zr->codec_mode);
@@ -840,7 +840,7 @@
 	//btor(ZR36057_VFESPFR_VCLKPol, ZR36057_VFESPFR);
 
 	/* code base address */
-	reg = virt_to_bus(zr->stat_com);
+	reg = zr->stat_com_bus;
 	btwrite(reg, ZR36057_JCBA);
 
 	/* FIFO threshold (FIFO is 160. double words) */
@@ -1181,12 +1181,28 @@
 			zr->stat_com[i + 1] =
 			    cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
 		}
+		if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+			if (zr->jpg_buffers.need_contiguous) {
+				pci_dma_sync_single_for_device(
+				    zr->pci_dev,
+				    le32_to_cpu(zr->jpg_buffers.buffer[frame].frag_tab[0]),
+				    zr->jpg_buffers.buffer_size,
+				    DMA_BIDIRECTIONAL);
+			} else {
+				pci_dma_sync_sg_for_device(zr->pci_dev,
+				    zr->jpg_buffers.buffer[frame].frag_tab_sg,
+				    zr->jpg_buffers.buffer_size / PAGE_SIZE,
+				    DMA_BIDIRECTIONAL);
+			}
+		}
 		zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
 		zr->jpg_dma_head++;
 
 	}
-	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS)
+
+	if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
 		zr->jpg_queued_num++;
+	}
 }
 
 /* when this is called the spinlock must be held */
@@ -1226,6 +1242,19 @@
 		do_gettimeofday(&buffer->bs.timestamp);
 
 		if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+			if (zr->jpg_buffers.need_contiguous) {
+				pci_dma_sync_single_for_cpu(
+				    zr->pci_dev,
+				    le32_to_cpu(zr->jpg_buffers.buffer[frame].frag_tab[0]),
+				    zr->jpg_buffers.buffer_size,
+				    DMA_BIDIRECTIONAL);
+			} else {
+				pci_dma_sync_sg_for_cpu(zr->pci_dev,
+				    zr->jpg_buffers.buffer[frame].frag_tab_sg,
+				    zr->jpg_buffers.buffer_size / PAGE_SIZE,
+				    DMA_BIDIRECTIONAL);
+			}
+			
 			buffer->bs.length = (stat_com & 0x7fffff) >> 1;
 
 			/* update sequence number with the help of the counter in stat_com */
diff -Naur linux-2.6.18_orig/drivers/media/video/zoran_driver.c linux-2.6.18/drivers/media/video/zoran_driver.c
--- linux-2.6.18_orig/drivers/media/video/zoran_driver.c	2008-05-22 16:57:47.000000000 +0200
+++ linux-2.6.18/drivers/media/video/zoran_driver.c	2008-05-22 16:58:44.000000000 +0200
@@ -49,6 +49,7 @@
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
+#include <linux/scatterlist.h>
 #include <linux/pci.h>
 #include <linux/vmalloc.h>
 #include <linux/wait.h>
@@ -341,12 +342,10 @@
 
 		//udelay(20);
 		if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
-			/* Use kmalloc */
-
-			mem =
-			    (unsigned char *) kmalloc(fh->v4l_buffers.
-						      buffer_size,
-						      GFP_KERNEL);
+			/* use pci alloc */
+			mem = pci_alloc_consistent(
+				zr->pci_dev, fh->v4l_buffers.buffer_size,
+				&fh->v4l_buffers.buffer[i].fbuffer_bus);
 			if (mem == 0) {
 				dprintk(1,
 					KERN_ERR
@@ -358,8 +357,6 @@
 			fh->v4l_buffers.buffer[i].fbuffer = mem;
 			fh->v4l_buffers.buffer[i].fbuffer_phys =
 			    virt_to_phys(mem);
-			fh->v4l_buffers.buffer[i].fbuffer_bus =
-			    virt_to_bus(mem);
 			for (off = 0; off < fh->v4l_buffers.buffer_size;
 			     off += PAGE_SIZE)
 				SetPageReserved(MAP_NR(mem + off));
@@ -367,11 +364,15 @@
 				KERN_INFO
 				"%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n",
 				ZR_DEVNAME(zr), i, (unsigned long) mem,
-				virt_to_bus(mem));
+				(unsigned long)fh->v4l_buffers.buffer[i].fbuffer_bus);
 		} else {
 #if defined(CONFIG_BIGPHYS_AREA)
 			/* Use bigphysarea_alloc_pages */
-
+			
+			dprintk(4,
+				KERN_INFO
+				"%s: Using BIGPHYS_AREA for fbuffer allocation\n"
+				ZR_DEVNAME(zr));
 			int n =
 			    (fh->v4l_buffers.buffer_size + PAGE_SIZE -
 			     1) / PAGE_SIZE;
@@ -387,23 +388,36 @@
 				v4l_fbuffer_free(file);
 				return -ENOBUFS;
 			}
+
 			fh->v4l_buffers.buffer[i].fbuffer = mem;
 			fh->v4l_buffers.buffer[i].fbuffer_phys =
 			    virt_to_phys(mem);
 			fh->v4l_buffers.buffer[i].fbuffer_bus =
-			    virt_to_bus(mem);
+			    pci_map_single(zr->pci_dev, mem, n * PAGE_SIZE,
+			    DMA_BIDIRECTIONAL);
+			if (! fh->v4l_buffers.buffer[i].fbuffer_bus) {
+			  dprintk(1,
+				  KERN_ERR
+				  "%s: Unable to map PCI space for v4l buffer.\n",
+				  ZR_DEVNAME(zr));
+			  v4l_fbuffer_free(file);
+			  return -ENOBUFS;
+			}
 			dprintk(4,
 				KERN_INFO
-				"%s: Bigphysarea frame %d mem 0x%x (bus: 0x%x)\n",
-				ZR_DEVNAME(zr), i, (unsigned) mem,
-				(unsigned) virt_to_bus(mem));
+				"%s: Bigphysarea frame %d mem 0x%lx (bus: 0x%lx)\n",
+				ZR_DEVNAME(zr), i, (unsigned long) mem,
+				(unsigned long) fh->v4l_buffers.buffer[i].fbuffer_bus);
 
 			/* Zero out the allocated memory */
 			memset(fh->v4l_buffers.buffer[i].fbuffer, 0,
 			       fh->v4l_buffers.buffer_size);
 #elif defined(BUZ_USE_HIMEM)
-
 			/* Use high memory which has been left at boot time */
+			dprintk(1,
+				KERN_WARNING
+				"%s: Using HIMEM hack for v4l buffers. Might break things.\n",
+				ZR_DEVNAME(zr));
 
 			/* Ok., Ok. this is an evil hack - we make
 			 * the assumption that physical addresses are
@@ -479,18 +493,30 @@
 			continue;
 
 		if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
-			mem = fh->v4l_buffers.buffer[i].fbuffer;
+			mem = (unsigned char*)fh->v4l_buffers.buffer[i].fbuffer_phys;
 			for (off = 0; off < fh->v4l_buffers.buffer_size;
-			     off += PAGE_SIZE)
+			    off += PAGE_SIZE)
 				ClearPageReserved(MAP_NR(mem + off));
-			kfree((void *) fh->v4l_buffers.buffer[i].fbuffer);
+			
+			pci_free_consistent(zr->pci_dev,
+			    fh->v4l_buffers.buffer_size, mem,
+			    fh->v4l_buffers.buffer[i].fbuffer_bus);
 		}
 #if defined(CONFIG_BIGPHYS_AREA)
-		else
+		else {
+			int n =
+				(fh->v4l_buffers.buffer_size + PAGE_SIZE -
+				1) / PAGE_SIZE;
 			bigphysarea_free_pages((void *) fh->v4l_buffers.
 					       buffer[i].fbuffer);
+			if (fh->v4l_buffers.buffer[i].fbuffer_bus) {
+				pci_unmap_single(zr->pci_dev,
+					fh->v4l_buffers.buffer[i].fbuffer_bus,
+					n * PAGE_SIZE, DMA_BIDIRECTIONAL);
+		}
 #endif
 		fh->v4l_buffers.buffer[i].fbuffer = NULL;
+		fh->v4l_buffers.buffer[i].fbuffer_bus = 0;
 	}
 
 	fh->v4l_buffers.allocated = 0;
@@ -536,8 +562,9 @@
 {
 	struct zoran_fh *fh = file->private_data;
 	struct zoran *zr = fh->zr;
-	int i, j, off;
-	unsigned long mem;
+	int i, j, off, sgcount = fh->jpg_buffers.buffer_size / PAGE_SIZE;
+	unsigned char *mem;
+	struct zoran_jpg_buffer *jpg_buffer;
 
 	/* we might have old buffers lying around */
 	if (fh->jpg_buffers.ready_to_be_freed) {
@@ -545,33 +572,38 @@
 	}
 
 	for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
-		if (fh->jpg_buffers.buffer[i].frag_tab)
+		jpg_buffer = fh->jpg_buffers.buffer + i;
+		if (jpg_buffer->frag_tab)
 			dprintk(2,
 				KERN_WARNING
 				"%s: jpg_fbuffer_alloc() - buffer %d allready allocated!?\n",
 				ZR_DEVNAME(zr), i);
 
 		/* Allocate fragment table for this buffer */
+		mem = pci_alloc_consistent(zr->pci_dev, 8 * sgcount,
+			&jpg_buffer->frag_tab_bus);
+		jpg_buffer->frag_tab_sg =
+			kzalloc((sizeof(struct scatterlist) + sizeof(void*))
+				* sgcount, GFP_KERNEL);
 
-		mem = get_zeroed_page(GFP_KERNEL);
-		if (mem == 0) {
+		if (mem == 0 || jpg_buffer->frag_tab_sg == 0) {
 			dprintk(1,
 				KERN_ERR
-				"%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n",
+				"%s: jpg_fbuffer_alloc() - kmalloc (frag_tab) failed for buffer %d\n",
 				ZR_DEVNAME(zr), i);
 			jpg_fbuffer_free(file);
 			return -ENOBUFS;
 		}
-		fh->jpg_buffers.buffer[i].frag_tab = (u32 *) mem;
-		fh->jpg_buffers.buffer[i].frag_tab_bus =
-		    virt_to_bus((void *) mem);
+		memset(mem, 0, 8 * sgcount);
+		jpg_buffer->frag_tab = (u32*)mem;
+		mem = (unsigned char*)jpg_buffer->frag_tab_sg;
+		jpg_buffer->frag_tab_allocs = (void**)(mem 
+		    + sgcount * sizeof(struct scatterlist));
 
-		//if (alloc_contig) {
 		if (fh->jpg_buffers.need_contiguous) {
-			mem =
-			    (unsigned long) kmalloc(fh->jpg_buffers.
-						    buffer_size,
-						    GFP_KERNEL);
+			dma_addr_t dma_mem;
+			mem = kmalloc(fh->jpg_buffers.buffer_size, GFP_KERNEL);
+			
 			if (mem == 0) {
 				dprintk(1,
 					KERN_ERR
@@ -580,19 +612,32 @@
 				jpg_fbuffer_free(file);
 				return -ENOBUFS;
 			}
-			fh->jpg_buffers.buffer[i].frag_tab[0] =
-			    cpu_to_le32(virt_to_bus((void *) mem));
-			fh->jpg_buffers.buffer[i].frag_tab[1] =
-			    cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1);
+			jpg_buffer->frag_tab_allocs[0] = mem;
 			for (off = 0; off < fh->jpg_buffers.buffer_size;
 			     off += PAGE_SIZE)
 				SetPageReserved(MAP_NR(mem + off));
+			
+			dma_mem = pci_map_single(zr->pci_dev, mem,
+			    fh->jpg_buffers.buffer_size,
+			    DMA_BIDIRECTIONAL);
+			if (dma_mem == 0) {
+				dprintk(1,
+					KERN_ERR
+					"%s: Mapping PCI DMA for jpeg buffer %d failed.\n",
+					ZR_DEVNAME(zr), i);
+				jpg_fbuffer_free(file);
+				return -ENOBUFS;
+			}
+			jpg_buffer->frag_tab[0] = cpu_to_le32(dma_mem);
+			jpg_buffer->frag_tab[1] = cpu_to_le32(((
+			    fh->jpg_buffers.buffer_size / 4) << 1) | 1);
 		} else {
 			/* jpg_bufsize is allreay page aligned */
-			for (j = 0;
-			     j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
-			     j++) {
-				mem = get_zeroed_page(GFP_KERNEL);
+			int real_fragments;
+
+			for (j = 0; j < sgcount; j++) {
+				mem = (unsigned char*)get_zeroed_page(
+				    GFP_KERNEL);
 				if (mem == 0) {
 					dprintk(1,
 						KERN_ERR
@@ -601,16 +646,35 @@
 					jpg_fbuffer_free(file);
 					return -ENOBUFS;
 				}
-
-				fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
-				    cpu_to_le32(virt_to_bus((void *) mem));
-				fh->jpg_buffers.buffer[i].frag_tab[2 * j +
-								   1] =
-				    cpu_to_le32((PAGE_SIZE / 4) << 1);
+				jpg_buffer->frag_tab_allocs[j] = mem;
+				sg_set_buf(jpg_buffer->frag_tab_sg + j, 
+					   mem, PAGE_SIZE);
 				SetPageReserved(MAP_NR(mem));
 			}
 
-			fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1);
+			real_fragments = pci_map_sg(zr->pci_dev,
+						    jpg_buffer->frag_tab_sg,
+						    sgcount,
+						    DMA_BIDIRECTIONAL);
+			if (real_fragments == 0) {
+				dprintk(1,
+					KERN_ERR
+					"%s: Mapping PCI DMA scatterlist for jpeg buffer %d failed.\n",
+					ZR_DEVNAME(zr), i);
+				jpg_fbuffer_free(file);
+				return -ENOBUFS;
+			}
+			jpg_buffer->scatterlist_entries = real_fragments;
+			for (j = 0; j < real_fragments; j++) {
+				jpg_buffer->frag_tab[2 * j] =
+				    cpu_to_le32(sg_dma_address(
+						jpg_buffer->frag_tab_sg + j));
+				jpg_buffer->frag_tab[2 * j + 1] =
+				    cpu_to_le32((sg_dma_len(
+					jpg_buffer->frag_tab_sg + j)
+					/ 4) << 1);
+			}
+			jpg_buffer->frag_tab[2 * j - 1] |= cpu_to_le32(1);
 		}
 	}
 
@@ -631,58 +695,75 @@
 {
 	struct zoran_fh *fh = file->private_data;
 	struct zoran *zr = fh->zr;
-	int i, j, off;
+	int i, j, off, sgcount = fh->jpg_buffers.buffer_size / PAGE_SIZE;
 	unsigned char *mem;
+	struct zoran_jpg_buffer *jpg_buffer;
 
 	dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr));
 
 	for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
-		if (!fh->jpg_buffers.buffer[i].frag_tab)
+		jpg_buffer = fh->jpg_buffers.buffer + i;
+		if (! jpg_buffer->frag_tab) {
+			if (jpg_buffer->frag_tab_sg) {
+				kfree(jpg_buffer->frag_tab_sg);
+			}
 			continue;
+		}
 
-		//if (alloc_contig) {
+		if (! jpg_buffer->frag_tab_sg) {
+			pci_free_consistent(zr->pci_dev, 8 * sgcount,
+			    jpg_buffer->frag_tab, jpg_buffer->frag_tab_bus);
+			continue;
+		}
+		
 		if (fh->jpg_buffers.need_contiguous) {
-			if (fh->jpg_buffers.buffer[i].frag_tab[0]) {
-				mem = (unsigned char *) bus_to_virt(le32_to_cpu(
-					fh->jpg_buffers.buffer[i].frag_tab[0]));
+			mem = jpg_buffer->frag_tab_allocs[0];
+			if (jpg_buffer->frag_tab[0]) {
+				pci_unmap_single(zr->pci_dev,
+				    le32_to_cpu(jpg_buffer->frag_tab[0]),
+				    fh->jpg_buffers.buffer_size,
+				    DMA_BIDIRECTIONAL);
+				jpg_buffer->frag_tab[0] = 0;
+				jpg_buffer->frag_tab[1] = 0;
+			}
+			if (mem) {
 				for (off = 0;
 				     off < fh->jpg_buffers.buffer_size;
 				     off += PAGE_SIZE)
 					ClearPageReserved(MAP_NR
 							  (mem + off));
 				kfree(mem);
-				fh->jpg_buffers.buffer[i].frag_tab[0] = 0;
-				fh->jpg_buffers.buffer[i].frag_tab[1] = 0;
 			}
 		} else {
-			for (j = 0;
-			     j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
-			     j++) {
-				if (!fh->jpg_buffers.buffer[i].
-				    frag_tab[2 * j])
+			if (fh->jpg_buffers.buffer[i].scatterlist_entries) {
+				pci_unmap_sg(zr->pci_dev,
+				    jpg_buffer->frag_tab_sg, sgcount,
+				    DMA_BIDIRECTIONAL);
+			}
+			for (j = 0; j < sgcount; j++) {
+				if (! jpg_buffer->frag_tab_allocs[j])
 					break;
-				ClearPageReserved(MAP_NR
-						  (bus_to_virt
-						   (le32_to_cpu
-						    (fh->jpg_buffers.
-						     buffer[i].frag_tab[2 *
-								       j]))));
+
+				ClearPageReserved(MAP_NR(
+				    jpg_buffer->frag_tab_allocs[j]));
 				free_page((unsigned long)
-					  bus_to_virt
-						  (le32_to_cpu
-						   (fh->jpg_buffers.
-						      buffer[i].
-						      frag_tab[2 * j])));
-				fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
-				    0;
-				fh->jpg_buffers.buffer[i].frag_tab[2 * j +
-								   1] = 0;
+					  jpg_buffer->frag_tab_allocs[j]);
+				jpg_buffer->frag_tab_allocs[j] = NULL;
+				jpg_buffer->frag_tab[2 * j] = 0;
+				jpg_buffer->frag_tab[2 * j + 1] = 0;
 			}
 		}
 
-		free_page((unsigned long) fh->jpg_buffers.buffer[i].
-			  frag_tab);
-		fh->jpg_buffers.buffer[i].frag_tab = NULL;
+		pci_free_consistent(zr->pci_dev, 8 * sgcount,
+				    jpg_buffer->frag_tab,
+				    jpg_buffer->frag_tab_bus);
+		jpg_buffer->frag_tab = NULL;
+		jpg_buffer->frag_tab_bus = 0;
+		
+		if (jpg_buffer->frag_tab_sg != NULL) {
+			kfree(jpg_buffer->frag_tab_sg);
+			jpg_buffer->frag_tab_sg = NULL;
+		}
 	}
 
 	fh->jpg_buffers.allocated = 0;
@@ -1232,6 +1313,7 @@
 		if (!zr->v4l_memgrab_active)
 			zr36057_overlay(zr, 0);
 		zr->overlay_mask = NULL;
+		zr->overlay_mask_bus = 0;
 	}
 
 	/* v4l capture */
@@ -1357,7 +1439,9 @@
 	/* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
 	 * on norm-change! */
 	fh->overlay_mask =
-	    kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+//	    kmalloc(((768 + 31) / 32) * 576 * 4, GFP_KERNEL);
+	    pci_alloc_consistent(zr->pci_dev, ((768 + 31) / 32) * 576 * 4,
+				 &fh->overlay_mask_bus);
 	if (!fh->overlay_mask) {
 		dprintk(1,
 			KERN_ERR
@@ -1440,6 +1524,7 @@
 		zr->v4l_overlay_active = 0;
 		zr36057_overlay(zr, 0);
 		zr->overlay_mask = NULL;
+		zr->overlay_mask_bus = 0;
 
 		/* capture off */
 		wake_up_interruptible(&zr->v4l_capq);
@@ -1456,7 +1541,8 @@
 	}
 
 	file->private_data = NULL;
-	kfree(fh->overlay_mask);
+	pci_free_consistent(zr->pci_dev, ((768 + 31) / 32) * 576 * 4,
+			    fh->overlay_mask, fh->overlay_mask_bus);
 	kfree(fh);
 
 	/* release locks on the i2c modules */
@@ -1738,6 +1824,7 @@
 		if (!zr->v4l_memgrab_active)
 			zr36057_overlay(zr, 0);
 		zr->overlay_mask = NULL;
+		zr->overlay_mask_bus = 0;
 	} else {
 		if (!zr->buffer.base || !fh->overlay_settings.is_set) {
 			dprintk(1,
@@ -1756,6 +1843,7 @@
 		zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
 		zr->v4l_overlay_active = 1;
 		zr->overlay_mask = fh->overlay_mask;
+		zr->overlay_mask_bus = fh->overlay_mask_bus;
 		zr->overlay_settings = fh->overlay_settings;
 		if (!zr->v4l_memgrab_active)
 			zr36057_overlay(zr, 1);
@@ -4548,41 +4636,32 @@
 		vma->vm_private_data = map;
 
 		for (i = first; i <= last; i++) {
-			for (j = 0;
-			     j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
-			     j++) {
-				fraglen =
-				    (le32_to_cpu(fh->jpg_buffers.buffer[i].
-				     frag_tab[2 * j + 1]) & ~1) << 1;
-				todo = size;
-				if (todo > fraglen)
-					todo = fraglen;
-				pos =
-				    le32_to_cpu((unsigned long) fh->jpg_buffers.
-				    buffer[i].frag_tab[2 * j]);
-				/* should just be pos on i386 */
-				page = virt_to_phys(bus_to_virt(pos))
-								>> PAGE_SHIFT;
-				if (remap_pfn_range(vma, start, page,
-							todo, PAGE_SHARED)) {
-					dprintk(1,
-						KERN_ERR
-						"%s: zoran_mmap(V4L) - remap_pfn_range failed\n",
-						ZR_DEVNAME(zr));
-					res = -EAGAIN;
-					goto jpg_mmap_unlock_and_return;
-				}
-				size -= todo;
-				start += todo;
-				if (size == 0)
-					break;
-				if (le32_to_cpu(fh->jpg_buffers.buffer[i].
-				    frag_tab[2 * j + 1]) & 1)
-					break;	/* was last fragment */
-			}
-			fh->jpg_buffers.buffer[i].map = map;
-			if (size == 0)
-				break;
+                        for (j = 0;
+                             j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+                             j++) {
+                                fraglen = PAGE_SIZE;
+                                todo = size;
+                                if (todo > fraglen)
+                                        todo = fraglen;
+                                page = virt_to_phys(fh->jpg_buffers.buffer[i].
+					frag_tab_allocs[j]) >> PAGE_SHIFT;
+                                if (remap_pfn_range(vma, start, page,
+                                                        todo, PAGE_SHARED)) {
+                                        dprintk(1,
+                                                KERN_ERR
+                                                "%s: zoran_mmap(V4L) - remap_pfn_range failed\n",
+                                                ZR_DEVNAME(zr));
+                                        res = -EAGAIN;
+                                        goto jpg_mmap_unlock_and_return;
+                                }
+                                size -= todo;
+                                start += todo;
+                                if (size == 0)
+                                        break;
+                        }
+                        fh->jpg_buffers.buffer[i].map = map;
+                        if (size == 0)
+                                break;
 
 		}
 	jpg_mmap_unlock_and_return:
diff -Naur linux-2.6.18_orig/drivers/media/video/zoran.h linux-2.6.18/drivers/media/video/zoran.h
--- linux-2.6.18_orig/drivers/media/video/zoran.h	2008-05-22 16:57:47.000000000 +0200
+++ linux-2.6.18/drivers/media/video/zoran.h	2008-05-22 16:58:44.000000000 +0200
@@ -296,17 +296,20 @@
 
 struct zoran_jpg_buffer {
 	struct zoran_mapping *map;
-	u32 *frag_tab;		/* addresses of frag table */
-	u32 frag_tab_bus;	/* same value cached to save time in ISR */
-	enum zoran_buffer_state state;	/* non-zero if corresponding buffer is in use in grab queue */
-	struct zoran_sync bs;	/* DONE: info to return to application */
+	u32 *frag_tab;			  /* virtual addresses of frag table */
+	dma_addr_t frag_tab_bus;	  /* same value cached to save time in ISR */
+	struct scatterlist *frag_tab_sg;  /* fragment table containing virtual addresses */
+	int scatterlist_entries;	  /* indicates whether the scatterlist is mapped and how many entries */
+	void **frag_tab_allocs;		  /* allocations done for the fragment table */
+	enum zoran_buffer_state state;	  /* non-zero if corresponding buffer is in use in grab queue */
+	struct zoran_sync bs;		  /* DONE: info to return to application */
 };
 
 struct zoran_v4l_buffer {
 	struct zoran_mapping *map;
 	char *fbuffer;		/* virtual  address of frame buffer */
 	unsigned long fbuffer_phys;	/* physical address of frame buffer */
-	unsigned long fbuffer_bus;	/* bus      address of frame buffer */
+	dma_addr_t fbuffer_bus;		/* bus      address of frame buffer */
 	enum zoran_buffer_state state;	/* state: unused/pending/done */
 	struct zoran_sync bs;	/* DONE: info to return to application */
 };
@@ -344,7 +347,8 @@
 	enum zoran_map_mode map_mode;	/* Flag which bufferset will map by next mmap() */
 
 	struct zoran_overlay_settings overlay_settings;
-	u32 *overlay_mask;	/* overlay mask */
+	u32 *overlay_mask;	 /* overlay mask */
+	dma_addr_t overlay_mask_bus;  /* bus address of overlay mask */
 	enum zoran_lock_activity overlay_active;	/* feature currently in use? */
 
 	struct zoran_v4l_settings v4l_settings;	/* structure with a lot of things to play with */
@@ -423,6 +427,7 @@
 	struct video_buffer buffer;	/* Current buffer params */
 	struct zoran_overlay_settings overlay_settings;
 	u32 *overlay_mask;	/* overlay mask */
+	dma_addr_t overlay_mask_bus;  /* bus address of overlay mask */
 	enum zoran_lock_activity overlay_active;	/* feature currently in use? */
 
 	wait_queue_head_t v4l_capq;
@@ -461,7 +466,8 @@
 	unsigned long jpg_queued_num;	/* count of frames queued since grab/play started */
 
 	/* zr36057's code buffer table */
-	u32 *stat_com;		/* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+	u32 *stat_com;		 /* stat_com[i] is indexed by dma_head/tail & BUZ_MASK_STAT_COM */
+	dma_addr_t stat_com_bus; /* stat_com mapping for pci bus */
 
 	/* (value & BUZ_MASK_FRAME) corresponds to index in pend[] queue */
 	int jpg_pend[BUZ_MAX_FRAME];