From: Michael Hunold <hunold@linuxtv.org>

- fix memory leak in page table handling

- minor coding style changes

- add simple resource management for video dmas (borrowed from saa7134)

- use resource management to lock video and vbi access which sometimes
  share the same video dmas

- honour return codes of extension functions in various places, when
  resources could not be locked

- remove remains of dead code which were commented out anyway 

- add new flag FORMAT_IS_PLANAR to indicate planar capture formats,
  needed for resource allocation


---

 drivers/media/common/saa7146_core.c  |   21 ++---
 drivers/media/common/saa7146_fops.c  |   80 ++++++++++++++++++--
 drivers/media/common/saa7146_hlp.c   |   71 ++++++------------
 drivers/media/common/saa7146_vbi.c   |   10 ++
 drivers/media/common/saa7146_video.c |  136 ++++++++++++++++++++++++++++-------
 include/media/saa7146.h              |    2 
 include/media/saa7146_vv.h           |   29 +++++--
 7 files changed, 250 insertions(+), 99 deletions(-)

diff -puN drivers/media/common/saa7146_core.c~dvb-02-update-saa7146 drivers/media/common/saa7146_core.c
--- 25/drivers/media/common/saa7146_core.c~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/drivers/media/common/saa7146_core.c	2004-01-18 16:26:08.000000000 -0800
@@ -137,7 +137,6 @@ static struct scatterlist* vmalloc_to_sg
 
 char *saa7146_vmalloc_build_pgtable(struct pci_dev *pci, long length, struct saa7146_pgtable *pt)
 {
-	struct scatterlist *slist = NULL;
 	int pages = (length+PAGE_SIZE-1)/PAGE_SIZE;
 	char *mem = vmalloc_32(length);
 	int slen = 0;
@@ -146,35 +145,36 @@ char *saa7146_vmalloc_build_pgtable(stru
 		return NULL;
 	}
 
-	if (!(slist = vmalloc_to_sg(mem, pages))) {
+	if (!(pt->slist = vmalloc_to_sg(mem, pages))) {
 		vfree(mem);
 		return NULL;
 	}
 
 	if (saa7146_pgtable_alloc(pci, pt)) {
-		kfree(slist);
+		kfree(pt->slist);
+		pt->slist = NULL;
 		vfree(mem);
 		return NULL;
 	}
 	
-	slen = pci_map_sg(pci,slist,pages,PCI_DMA_FROMDEVICE);
-	if (0 != saa7146_pgtable_build_single(pci, pt, slist, slen)) {
+	slen = pci_map_sg(pci,pt->slist,pages,PCI_DMA_FROMDEVICE);
+	if (0 != saa7146_pgtable_build_single(pci, pt, pt->slist, slen)) {
 		return NULL;
 	}
 
-	/* fixme: here's a memory leak: slist never gets freed by any other
-	   function ...*/
 	return mem;
 }
 
 void saa7146_pgtable_free(struct pci_dev *pci, struct saa7146_pgtable *pt)
 {
-//fm	DEB_EE(("pci:%p, pt:%p\n",pci,pt));
-
 	if (NULL == pt->cpu)
 		return;
 	pci_free_consistent(pci, pt->size, pt->cpu, pt->dma);
 	pt->cpu = NULL;
+	if (NULL != pt->slist) {
+		kfree(pt->slist);
+		pt->slist = NULL;
+	}
 }
 
 int saa7146_pgtable_alloc(struct pci_dev *pci, struct saa7146_pgtable *pt)
@@ -182,11 +182,8 @@ int saa7146_pgtable_alloc(struct pci_dev
         u32          *cpu;
         dma_addr_t   dma_addr;
 
-//fm	DEB_EE(("pci:%p, pt:%p\n",pci,pt));
-
 	cpu = pci_alloc_consistent(pci, SAA7146_PGTABLE_SIZE, &dma_addr);
 	if (NULL == cpu) {
-//fm		ERR(("pci_alloc_consistent() failed."));
 		return -ENOMEM;
 	}
 	pt->size = SAA7146_PGTABLE_SIZE;
diff -puN drivers/media/common/saa7146_fops.c~dvb-02-update-saa7146 drivers/media/common/saa7146_fops.c
--- 25/drivers/media/common/saa7146_fops.c~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/drivers/media/common/saa7146_fops.c	2004-01-18 16:26:08.000000000 -0800
@@ -2,6 +2,65 @@
 
 #define BOARD_CAN_DO_VBI(dev)   (dev->revision != 0 && dev->vv_data->vbi_minor != -1) 
 
+/****************************************************************************/
+/* resource management functions, shamelessly stolen from saa7134 driver */
+
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+
+	if (fh->resources & bit)
+		/* have it already allocated */
+		return 1;
+
+	/* is it free? */
+	DEB_D(("getting lock...\n"));
+	down(&dev->lock);
+	DEB_D(("got lock\n"));
+	if (vv->resources & bit) {
+		DEB_D(("locked! vv->resources:0x%02x, we want:0x%02x\n",vv->resources,bit));
+		/* no, someone else uses it */
+		up(&dev->lock);
+		return 0;
+	}
+	/* it's free, grab it */
+	fh->resources  |= bit;
+	vv->resources |= bit;
+	DEB_D(("res: get %d\n",bit));
+	up(&dev->lock);
+	return 1;
+}
+
+int saa7146_res_check(struct saa7146_fh *fh, unsigned int bit)
+{
+	return (fh->resources & bit);
+}
+
+int saa7146_res_locked(struct saa7146_dev *dev, unsigned int bit)
+{
+	struct saa7146_vv *vv = dev->vv_data;
+	return (vv->resources & bit);
+}
+
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits)
+{
+	struct saa7146_dev *dev = fh->dev;
+	struct saa7146_vv *vv = dev->vv_data;
+
+	if ((fh->resources & bits) != bits)
+		BUG();
+
+	DEB_D(("getting lock...\n"));
+	down(&dev->lock);
+	DEB_D(("got lock\n"));
+	fh->resources  &= ~bits;
+	vv->resources &= ~bits;
+	DEB_D(("res: put %d\n",bits));
+	up(&dev->lock);
+}
+
+
 /********************************************************************************/
 /* common dma functions */
 
@@ -216,29 +275,32 @@ static int fops_open(struct inode *inode
 	}
 	memset(fh,0,sizeof(*fh));
 	
-	// FIXME: do we need to increase *our* usage count?
-
-	if( 0 == try_module_get(dev->ext->module)) {
-		result = -EINVAL;
-		goto out;
-	}
-
 	file->private_data = fh;
 	fh->dev = dev;
 	fh->type = type;
 
 	if( fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
 		DEB_S(("initializing vbi...\n"));
-		saa7146_vbi_uops.open(dev,file);
+		result = saa7146_vbi_uops.open(dev,file);
 	} else {
 		DEB_S(("initializing video...\n"));
-		saa7146_video_uops.open(dev,file);
+		result = saa7146_video_uops.open(dev,file);
+	}
+
+	if (0 != result) {
+		goto out;
+	}
+
+	if( 0 == try_module_get(dev->ext->module)) {
+		result = -EINVAL;
+		goto out;
 	}
 
 	result = 0;
 out:
 	if( fh != 0 && result != 0 ) {
 		kfree(fh);
+		file->private_data = NULL;
 	}
 	up(&saa7146_devices_lock);
         return result;
diff -puN drivers/media/common/saa7146_hlp.c~dvb-02-update-saa7146 drivers/media/common/saa7146_hlp.c
--- 25/drivers/media/common/saa7146_hlp.c~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/drivers/media/common/saa7146_hlp.c	2004-01-18 16:26:08.000000000 -0800
@@ -152,8 +152,7 @@ static int calculate_h_scale_registers(s
 	if( 1 == xpsc ) {
 		xacm = 1;
 		dcgx = 0;
-	}
-	else {
+	} else {
 		xacm = 0;
 		/* get best match in the table of attenuations
 		   for horizontal scaling */
@@ -268,8 +267,7 @@ static int calculate_v_scale_registers(s
 		ype = ysci / 16;
 		ypo = ype + (ysci / 64);
 		
-	}
-	else {
+	} else {
 		yacm = 1;	
 
 		/* calculate scaling increment */
@@ -285,8 +283,7 @@ static int calculate_v_scale_registers(s
 						... */
 		if ( ysci < 512) {
 			yacl = 0;
-		}
-		else {
+		} else {
 			yacl = ( ysci / (1024 - ysci) );
 		}
 
@@ -488,33 +485,31 @@ static void saa7146_disable_clipping(str
  	saa7146_write(dev, MC2, (MASK_05 | MASK_21));
  
  	/* disable video dma2 */
-	saa7146_write(dev, MC1, (MASK_21));
+	saa7146_write(dev, MC1, MASK_21);
 }
 
-static void saa7146_set_clipping_rect(struct saa7146_dev *dev, struct saa7146_fh *fh)
+static void saa7146_set_clipping_rect(struct saa7146_fh *fh)
 {
+	struct saa7146_dev *dev = fh->dev;
 	enum v4l2_field field = fh->ov.win.field;
-	int clipcount = fh->ov.nclips;
-	
 	struct	saa7146_video_dma vdma2;
-
-	u32 clip_format	= saa7146_read(dev, CLIP_FORMAT_CTRL);
-	u32 arbtr_ctrl	= saa7146_read(dev, PCI_BT_V1);
-
-	// fixme: is this used at all? SAA7146_CLIPPING_RECT_INVERTED;
-	u32 type = SAA7146_CLIPPING_RECT;
+	u32 clip_format;
+	u32 arbtr_ctrl;
 
 	/* check clipcount, disable clipping if clipcount == 0*/
-	if( clipcount == 0 ) {
+	if( fh->ov.nclips == 0 ) {
 		saa7146_disable_clipping(dev);
 		return;
 	}
 
+	clip_format = saa7146_read(dev, CLIP_FORMAT_CTRL);
+	arbtr_ctrl = saa7146_read(dev, PCI_BT_V1);
+
 	calculate_clipping_registers_rect(dev, fh, &vdma2, &clip_format, &arbtr_ctrl, field);
 
 	/* set clipping format */
 	clip_format &= 0xffff0008;
-	clip_format |= (type << 4);
+	clip_format |= (SAA7146_CLIPPING_RECT << 4);
 
 	/* prepare video dma2 */
 	saa7146_write(dev, BASE_EVEN2,		vdma2.base_even);
@@ -660,25 +655,28 @@ void saa7146_set_hps_source_and_sync(str
 	vv->current_hps_sync = sync;
 } 
 
-/* reprogram hps, enable(1) / disable(0) video */
-void saa7146_set_overlay(struct saa7146_dev *dev, struct saa7146_fh *fh, int v)
+int saa7146_enable_overlay(struct saa7146_fh *fh)
 {
+	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
 
-	/* enable ? */
-	if( 0 == v) {
-		/* disable video dma1 */
-      		saa7146_write(dev, MC1, MASK_22);
-		return;
-	}
-
 	saa7146_set_window(dev, fh->ov.win.w.width, fh->ov.win.w.height, fh->ov.win.field);
 	saa7146_set_position(dev, fh->ov.win.w.left, fh->ov.win.w.top, fh->ov.win.w.height, fh->ov.win.field);
 	saa7146_set_output_format(dev, vv->ov_fmt->trans);
-	saa7146_set_clipping_rect(dev, fh);
+	saa7146_set_clipping_rect(fh);
 
 	/* enable video dma1 */
 	saa7146_write(dev, MC1, (MASK_06 | MASK_22));
+	return 0;
+}
+
+void saa7146_disable_overlay(struct saa7146_fh *fh)
+{
+	struct saa7146_dev *dev = fh->dev;
+
+	/* disable clipping + video dma1 */
+ 	saa7146_disable_clipping(dev);
+	saa7146_write(dev, MC1, MASK_22);
 }		
 
 void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) 
@@ -692,15 +690,8 @@ void saa7146_write_out_dma(struct saa714
 	/* calculate starting address */
 	where  = (which-1)*0x18;
 
-/*
-	if( 0 != (dev->ext_vv_data->flags & SAA7146_EXT_SWAP_ODD_EVEN)) {
-		saa7146_write(dev, where, 	vdma->base_even);
-		saa7146_write(dev, where+0x04, 	vdma->base_odd);
-	} else {
-*/
 		saa7146_write(dev, where, 	vdma->base_odd);
 		saa7146_write(dev, where+0x04, 	vdma->base_even);
-//	}
 	saa7146_write(dev, where+0x08, 	vdma->prot_addr);
 	saa7146_write(dev, where+0x0c, 	vdma->pitch);
 	saa7146_write(dev, where+0x10, 	vdma->base_page);
@@ -937,7 +928,7 @@ static int calculate_video_dma_grab_plan
 	}	
 
 	saa7146_write_out_dma(dev, 1, &vdma1);
-	if( sfmt->swap != 0 ) {
+	if( (sfmt->flags & FORMAT_BYTE_SWAP) != 0 ) {
 		saa7146_write_out_dma(dev, 3, &vdma2);
 		saa7146_write_out_dma(dev, 2, &vdma3);
 	} else {
@@ -955,13 +946,6 @@ static void program_capture_engine(struc
 	unsigned long e_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_E_FID_A : CMD_E_FID_B;
 	unsigned long o_wait = vv->current_hps_sync == SAA7146_HPS_SYNC_PORT_A ? CMD_O_FID_A : CMD_O_FID_B;
 
-/*
-	if( 0 != (dev->ext_vv_data->flags & SAA7146_EXT_SWAP_ODD_EVEN)) {
-		unsigned long tmp = e_wait;
-		e_wait = o_wait;
-		o_wait = tmp;
-	}
-*/
 	/* wait for o_fid_a/b / e_fid_a/b toggle only if rps register 0 is not set*/
 	WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | o_wait);
 	WRITE_RPS0(CMD_PAUSE | CMD_OAN | CMD_SIG0 | e_wait);
@@ -1038,7 +1022,6 @@ void saa7146_set_capture(struct saa7146_
 
 	saa7146_set_window(dev, buf->fmt->width, buf->fmt->height, buf->fmt->field);
 	saa7146_set_output_format(dev, sfmt->trans);
-	saa7146_disable_clipping(dev);
 
 	if ( vv->last_field == V4L2_FIELD_INTERLACED ) {
 	} else if ( vv->last_field == V4L2_FIELD_TOP ) {
diff -puN drivers/media/common/saa7146_vbi.c~dvb-02-update-saa7146 drivers/media/common/saa7146_vbi.c
--- 25/drivers/media/common/saa7146_vbi.c~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/drivers/media/common/saa7146_vbi.c	2004-01-18 16:26:08.000000000 -0800
@@ -366,7 +366,7 @@ static void vbi_init(struct saa7146_dev 
 	init_waitqueue_head(&vv->vbi_wq);
 }
 
-static void vbi_open(struct saa7146_dev *dev, struct file *file)
+static int vbi_open(struct saa7146_dev *dev, struct file *file)
 {
 	struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
 	
@@ -375,6 +375,12 @@ static void vbi_open(struct saa7146_dev 
 	
 	DEB_VBI(("dev:%p, fh:%p\n",dev,fh));
 
+	ret = saa7146_res_get(fh, RESOURCE_DMA3_BRS);
+	if (0 == ret) {
+		DEB_S(("cannot get vbi RESOURCE_DMA3_BRS resource\n"));
+		return -EBUSY;
+	}
+
 	/* adjust arbitrition control for video dma 3 */
 	arbtr_ctrl &= ~0x1f0000;
 	arbtr_ctrl |=  0x1d0000;
@@ -419,6 +425,7 @@ static void vbi_open(struct saa7146_dev 
 
 	/* upload brs register */
 	saa7146_write(dev, MC2, (MASK_08|MASK_24));		
+	return 0;
 }
 
 static void vbi_close(struct saa7146_dev *dev, struct file *file)
@@ -430,6 +437,7 @@ static void vbi_close(struct saa7146_dev
 	if( fh == vv->vbi_streaming ) {
 		vbi_stop(fh, file);
 	}
+	saa7146_res_free(fh, RESOURCE_DMA3_BRS);
 }
 
 static void vbi_irq_done(struct saa7146_dev *dev, unsigned long status)
diff -puN drivers/media/common/saa7146_video.c~dvb-02-update-saa7146 drivers/media/common/saa7146_video.c
--- 25/drivers/media/common/saa7146_video.c~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/drivers/media/common/saa7146_video.c	2004-01-18 16:26:08.000000000 -0800
@@ -12,48 +12,55 @@ static struct saa7146_format formats[] =
 		.pixelformat	= V4L2_PIX_FMT_RGB332,
 		.trans 		= RGB08_COMPOSED,
 		.depth		= 8,
+		.flags		= 0,
 	}, {
-		.name 		= "RGB-16 (5/B-6/G-5/R)", 	/* really? */
+		.name 		= "RGB-16 (5/B-6/G-5/R)",
 		.pixelformat	= V4L2_PIX_FMT_RGB565,
 		.trans 		= RGB16_COMPOSED,
 		.depth		= 16,
+		.flags		= 0,
 	}, {
 		.name 		= "RGB-24 (B-G-R)",
 		.pixelformat	= V4L2_PIX_FMT_BGR24,
 		.trans 		= RGB24_COMPOSED,
 		.depth		= 24,
+		.flags		= 0,
 	}, {
 		.name 		= "RGB-32 (B-G-R)",
 		.pixelformat	= V4L2_PIX_FMT_BGR32,
 		.trans 		= RGB32_COMPOSED,
 		.depth		= 32,
+		.flags		= 0,
 	}, {
 		.name 		= "Greyscale-8",
 		.pixelformat	= V4L2_PIX_FMT_GREY,
 		.trans 		= Y8,
 		.depth		= 8,
+		.flags		= 0,
 	}, {
 		.name 		= "YUV 4:2:2 planar (Y-Cb-Cr)",
 		.pixelformat	= V4L2_PIX_FMT_YUV422P,
 		.trans 		= YUV422_DECOMPOSED,
 		.depth		= 16,
-		.swap		= 1,
+		.flags		= FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
 	}, {
 		.name 		= "YVU 4:2:0 planar (Y-Cb-Cr)",
 		.pixelformat	= V4L2_PIX_FMT_YVU420,
 		.trans 		= YUV420_DECOMPOSED,
 		.depth		= 12,
-		.swap		= 1,
+		.flags		= FORMAT_BYTE_SWAP|FORMAT_IS_PLANAR,
 	}, {
 		.name 		= "YUV 4:2:0 planar (Y-Cb-Cr)",
 		.pixelformat	= V4L2_PIX_FMT_YUV420,
 		.trans 		= YUV420_DECOMPOSED,
 		.depth		= 12,
+		.flags		= FORMAT_IS_PLANAR,
 	}, {
 		.name 		= "YUV 4:2:2 (U-Y-V-Y)",
 		.pixelformat	= V4L2_PIX_FMT_UYVY,
 		.trans 		= YUV422_COMPOSED,
 		.depth		= 16,
+		.flags		= 0,
 	}
 };
 
@@ -243,13 +250,13 @@ int saa7146_start_preview(struct saa7146
 {
 	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
-	int err = 0;
+	int ret = 0, err = 0;
 
 	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
 	/* check if we have overlay informations */
 	if( NULL == fh->ov.fh ) {
-		DEB_D(("not overlay data available. try S_FMT first.\n"));
+		DEB_D(("no overlay data available. try S_FMT first.\n"));
 		return -EAGAIN;
 	}
 
@@ -280,7 +287,11 @@ int saa7146_start_preview(struct saa7146
 		fh->ov.win.w.left,fh->ov.win.w.top,
 		vv->ov_fmt->name,v4l2_field_names[fh->ov.win.field]));
 	
-	saa7146_set_overlay(dev, fh, 1);
+	if (0 != (ret = saa7146_enable_overlay(fh))) {
+		vv->ov_data = NULL;
+		DEB_D(("enabling overlay failed: %d\n",ret));
+		return ret;
+	}
 
 	return 0;
 }
@@ -290,7 +301,7 @@ int saa7146_stop_preview(struct saa7146_
 	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
 
-	DEB_EE(("saa7146.o: saa7146_stop_preview()\n"));
+	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
 	/* check if overlay is running */
 	if( 0 == vv->ov_data ) {
@@ -300,11 +311,11 @@ int saa7146_stop_preview(struct saa7146_
 
 	if( fh != vv->ov_data->fh ) {
 		DEB_D(("overlay is active, but for another open.\n"));
-		return -EBUSY;
+		return 0;
 	}
 
-	saa7146_set_overlay(dev, fh, 0);
 	vv->ov_data = NULL;
+	saa7146_disable_overlay(fh);
 	
 	return 0;
 }
@@ -675,21 +686,37 @@ static int video_begin(struct saa7146_fh
 {
 	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
+	struct saa7146_format *fmt = NULL;
 	unsigned long flags;
+	unsigned int resource;
+	int ret = 0;
 
 	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
 	if( fh == vv->streaming ) {
 		DEB_S(("already capturing.\n"));
-		return 0;
+		return -EBUSY;
 	}
 	if( vv->streaming != 0 ) {
 		DEB_S(("already capturing, but in another open.\n"));
 		return -EBUSY;
 	}
 
-	/* fixme: check for planar formats here, if we will interfere with
-	   vbi capture for example */	
+	fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+	/* we need to have a valid format set here */
+	BUG_ON(NULL == fmt);
+
+	if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+		resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+	} else {
+		resource = RESOURCE_DMA1_HPS;
+	}
+
+	ret = saa7146_res_get(fh, resource);
+	if (0 == ret) {
+		DEB_S(("cannot get capture resource %d\n",resource));
+		return -EBUSY;
+	}
 
 	spin_lock_irqsave(&dev->slock,flags);
 
@@ -708,8 +735,10 @@ static int video_end(struct saa7146_fh *
 {
 	struct saa7146_dev *dev = fh->dev;
 	struct saa7146_vv *vv = dev->vv_data;
+	struct saa7146_format *fmt = NULL;
 	unsigned long flags;
-	
+	unsigned int resource;
+	u32 dmas = 0;
 	DEB_EE(("dev:%p, fh:%p\n",dev,fh));
 
 	if( vv->streaming != fh ) {
@@ -717,6 +746,19 @@ static int video_end(struct saa7146_fh *
 		return -EINVAL;
 	}
 
+	fmt = format_by_fourcc(dev,fh->video_fmt.pixelformat);
+	/* we need to have a valid format set here */
+	BUG_ON(NULL == fmt);
+
+	if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+		resource = RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP|RESOURCE_DMA3_BRS;
+		dmas = MASK_22 | MASK_21 | MASK_20;
+	} else {
+		resource = RESOURCE_DMA1_HPS;
+		dmas = MASK_20;
+	}
+	saa7146_res_free(fh, resource);
+
 	spin_lock_irqsave(&dev->slock,flags);
 
 	/* disable rps0  */
@@ -725,14 +767,8 @@ static int video_end(struct saa7146_fh *
 	/* disable rps0 irqs */
 	IER_DISABLE(dev, MASK_27);
 
-	// fixme: only used formats here!
-	/* fixme: look at planar formats here, especially at the
-	   shutdown of planar formats! */
-
 	/* shut down all used video dma transfers */
-	/* fixme: what about the budget-dvb cards? they use
-	   video-dma3, but video_end should not get called anyway ...*/
-	saa7146_write(dev, MC1, 0x00700000);
+	saa7146_write(dev, MC1, dmas);
 
 	vv->streaming = NULL;
 
@@ -849,8 +885,12 @@ int saa7146_video_do_ioctl(struct inode 
 			return -EINVAL;
 		}
 		
-		down(&dev->lock);
+		/* planar formats are not allowed for overlay video, clipping and video dma would clash */
+		if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+			DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
+		}
 
+		down(&dev->lock);
 		if( vv->ov_data != NULL ) {
 			ov_fh = vv->ov_data->fh;
 			saa7146_stop_preview(ov_fh);
@@ -1002,7 +1042,9 @@ int saa7146_video_do_ioctl(struct inode 
 			return -EBUSY;
 		}
 
+		DEB_D(("before getting lock...\n"));
 		down(&dev->lock);
+		DEB_D(("got lock\n"));
 
 		if( vv->ov_data != NULL ) {
 			ov_fh = vv->ov_data->fh;
@@ -1047,22 +1089,33 @@ int saa7146_video_do_ioctl(struct inode 
 		if( 0 != on ) {
 			if( vv->ov_data != NULL ) {
 				if( fh != vv->ov_data->fh) {
+					DEB_D(("overlay already active in another open\n"));
 					return -EAGAIN;
 				}
 			}
+
+			if (0 == saa7146_res_get(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+				DEB_D(("cannot get overlay resources\n"));
+				return -EBUSY;
+			}
+
 			spin_lock_irqsave(&dev->slock,flags);
 			err = saa7146_start_preview(fh);
 			spin_unlock_irqrestore(&dev->slock,flags);
-		} else {
+			return err;
+		}
+
 			if( vv->ov_data != NULL ) {
 				if( fh != vv->ov_data->fh) {
+				DEB_D(("overlay is active, but in another open\n"));
 					return -EAGAIN;
 				}
 			}
 			spin_lock_irqsave(&dev->slock,flags);
 			err = saa7146_stop_preview(fh);
 			spin_unlock_irqrestore(&dev->slock,flags);
-		}
+		/* free resources */
+		saa7146_res_free(fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
 		return err;
 	}
 	case VIDIOC_REQBUFS: {
@@ -1093,7 +1146,13 @@ int saa7146_video_do_ioctl(struct inode 
 		int *type = arg;
 		DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
 
-		if( 0 != (err = video_begin(fh))) {
+		if( fh == vv->streaming ) {
+			DEB_D(("already capturing.\n"));
+			return 0;
+		}
+
+		err = video_begin(fh);
+		if( 0 != err) {
 				return err;
 			}
 		err = videobuf_streamon(file,q);
@@ -1103,6 +1162,12 @@ int saa7146_video_do_ioctl(struct inode 
 		int *type = arg;
 
 		DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
+
+		if( fh != vv->streaming ) {
+			DEB_D(("this open is not capturing.\n"));
+			return -EINVAL;
+		}
+
 		err = videobuf_streamoff(file,q);
 		video_end(fh, file);
 		return err;
@@ -1309,7 +1374,7 @@ static void video_init(struct saa7146_de
 }
 
 
-static void video_open(struct saa7146_dev *dev, struct file *file)
+static int video_open(struct saa7146_dev *dev, struct file *file)
 {
 	struct saa7146_fh *fh = (struct saa7146_fh *)file->private_data;
 	struct saa7146_format *sfmt;
@@ -1329,6 +1394,8 @@ static void video_open(struct saa7146_de
 			    sizeof(struct saa7146_buf));
 
 	init_MUTEX(&fh->video_q.lock);
+
+	return 0;
 }
 
 
@@ -1381,20 +1448,35 @@ static ssize_t video_read(struct file *f
 
 	DEB_EE(("called.\n"));
 
+	/* fixme: should we allow read() captures while streaming capture? */
+	if( 0 != vv->streaming ) {
+		DEB_S(("already capturing.\n"));
+		return -EBUSY;
+	}
+
+	/* stop any active overlay */
 	if( vv->ov_data != NULL ) {
 		ov_fh = vv->ov_data->fh;
 		saa7146_stop_preview(ov_fh);
+		saa7146_res_free(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
 		restart_overlay = 1;
 	}
 
-	if( 0 != video_begin(fh)) {
-		return -EAGAIN;
+	ret = video_begin(fh);
+	if( 0 != ret) {
+		goto out;
 	}
+
 	ret = videobuf_read_one(file,&fh->video_q , data, count, ppos);
 	video_end(fh, file);
 
+out:
 	/* restart overlay if it was active before */
 	if( 0 != restart_overlay ) {
+		if (0 == saa7146_res_get(ov_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP)) {
+			DEB_D(("cannot get overlay resources again!\n"));
+			BUG();
+		}
 		saa7146_start_preview(ov_fh);
 	}
 	
diff -puN include/media/saa7146.h~dvb-02-update-saa7146 include/media/saa7146.h
--- 25/include/media/saa7146.h~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/include/media/saa7146.h	2004-01-18 16:26:08.000000000 -0800
@@ -67,6 +67,8 @@ struct saa7146_pgtable {
 	dma_addr_t	dma;
 	/* used for offsets for u,v planes for planar capture modes */
 	unsigned long	offset;
+	/* used for custom pagetables (used for example by budget dvb cards) */
+	struct scatterlist *slist;
 };
 
 struct saa7146_pci_extension_data {
diff -puN include/media/saa7146_vv.h~dvb-02-update-saa7146 include/media/saa7146_vv.h
--- 25/include/media/saa7146_vv.h~dvb-02-update-saa7146	2004-01-18 16:26:08.000000000 -0800
+++ 25-akpm/include/media/saa7146_vv.h	2004-01-18 16:26:08.000000000 -0800
@@ -26,12 +26,15 @@ struct	saa7146_video_dma {
 	u32 num_line_byte;
 };
 
+#define FORMAT_BYTE_SWAP	0x1
+#define FORMAT_IS_PLANAR	0x2
+
 struct saa7146_format {
 	char	*name;
-	int   	pixelformat;
+	u32   	pixelformat;
 	u32	trans;
 	u8	depth;
-	int	swap;
+	u8	flags;
 };
 
 struct saa7146_standard
@@ -97,6 +100,8 @@ struct saa7146_fh {
 	struct videobuf_queue	vbi_q;
 	struct v4l2_vbi_format	vbi_fmt;
 	struct timer_list	vbi_read_timeout;
+
+	unsigned int resources;	/* resource management for device open */
 };
 
 struct saa7146_vv
@@ -136,6 +141,8 @@ struct saa7146_vv
 	int 	current_hps_sync;
 
 	struct saa7146_dma	d_clipping;	/* pointer to clipping memory */
+
+	unsigned int resources;	/* resource management for device */
 };
 
 #define SAA7146_EXCLUSIVE	0x1
@@ -149,7 +156,6 @@ struct saa7146_extension_ioctls
 };
 
 /* flags */
-// #define SAA7146_EXT_SWAP_ODD_EVEN	0x1     /* needs odd/even fields swapped */
 #define SAA7146_USE_PORT_B_FOR_VBI	0x2     /* use input port b for vbi hardware bug workaround */
 
 struct saa7146_ext_vv
@@ -171,7 +177,7 @@ struct saa7146_ext_vv
 
 struct saa7146_use_ops  {
         void (*init)(struct saa7146_dev *, struct saa7146_vv *);
-        void(*open)(struct saa7146_dev *, struct file *);
+        int(*open)(struct saa7146_dev *, struct file *);
         void (*release)(struct saa7146_dev *, struct file *);
         void (*irq_done)(struct saa7146_dev *, unsigned long status);
 	ssize_t (*read)(struct file *, char *, size_t, loff_t *);
@@ -189,9 +195,10 @@ void saa7146_dma_free(struct saa7146_dev
 int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv);
 int saa7146_vv_release(struct saa7146_dev* dev);
 
-
 /* from saa7146_hlp.c */
-void saa7146_set_overlay(struct saa7146_dev *dev, struct saa7146_fh *fh, int v);
+int saa7146_enable_overlay(struct saa7146_fh *fh);
+void saa7146_disable_overlay(struct saa7146_fh *fh);
+
 void saa7146_set_capture(struct saa7146_dev *dev, struct saa7146_buf *buf, struct saa7146_buf *next);
 void saa7146_write_out_dma(struct saa7146_dev* dev, int which, struct saa7146_video_dma* vdma) ;
 void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sync);
@@ -205,6 +212,16 @@ int saa7146_stop_preview(struct saa7146_
 /* from saa7146_vbi.c */
 extern struct saa7146_use_ops saa7146_vbi_uops;
 
+/* resource management functions */
+int saa7146_res_get(struct saa7146_fh *fh, unsigned int bit);
+int saa7146_res_check(struct saa7146_fh *fh, unsigned int bit);
+int saa7146_res_locked(struct saa7146_dev *dev, unsigned int bit);
+void saa7146_res_free(struct saa7146_fh *fh, unsigned int bits);
+
+#define RESOURCE_DMA1_HPS	0x1
+#define RESOURCE_DMA2_CLP	0x2
+#define RESOURCE_DMA3_BRS	0x4
+
 /* saa7146 source inputs */
 #define SAA7146_HPS_SOURCE_PORT_A	0x00
 #define SAA7146_HPS_SOURCE_PORT_B	0x01

_