diff -u orig/saa7134-core.c ./saa7134-core.c
--- orig/saa7134-core.c	Mon May  5 18:26:17 2003
+++ ./saa7134-core.c	Sun Jun 15 12:04:05 2003
@@ -877,6 +877,7 @@
 	/* everything worked */
 	list_add_tail(&dev->devlist,&saa7134_devlist);
 	pci_set_drvdata(pci_dev,dev);
+	init_waitqueue_head(&dev->chn_ev_q);
 	saa7134_devcount++;
 	return 0;
 
diff -u orig/saa7134-video.c ./saa7134-video.c
--- orig/saa7134-video.c	Wed May 14 12:26:29 2003
+++ ./saa7134-video.c	Sun Jun 15 12:53:43 2003
@@ -24,6 +24,7 @@
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/wait.h>
 
 #include "saa7134-reg.h"
 #include "saa7134.h"
@@ -416,6 +417,151 @@
 	up(&dev->lock);
 }
 
+/* ----------------------------------------------------------------------- */
+/* channel event notification                                              */
+
+static
+int wait_chn_event(struct saa7134_dev *dev, struct saa7134_fh *fh, int non_blocking)
+{
+	int retval = 0;
+	DECLARE_WAITQUEUE(wait, current);
+
+	add_wait_queue(&dev->chn_ev_q, &wait);
+	while ( (memcmp(dev->chn_ev_idx, fh->chn_ev_idx, sizeof(dev->chn_ev_idx)) == 0) &&
+		(fh->chn_ev_poll != 0) ) {
+
+		if (non_blocking) {
+			retval = -EAGAIN;
+			break;
+		}
+		current->state = TASK_INTERRUPTIBLE;
+		schedule();
+		if (signal_pending(current)) {
+			dprintk("S_CHNEVENT waiton(NONBL %d): -EINTR\n", non_blocking);
+			retval = -EINTR;
+			break;
+		}
+	}
+	remove_wait_queue(&dev->chn_ev_q, &wait);
+
+	return retval;
+}
+
+static
+void post_chn_event(struct saa7134_dev *dev, enum v4l2_chn_event_bit ev)
+{
+	/* note: dev mutex must be locked by the caller */
+
+	if (ev < V4L2_CHN_EV_COUNT) {
+		dev->chn_ev_idx[ev]++;
+
+		wake_up(&dev->chn_ev_q);
+	}
+}
+
+static
+v4l2_chn_event_mask get_chn_events(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+	v4l2_chn_event_mask ev_mask;
+	int i, bit;
+
+	ev_mask = 0;
+
+	down(&dev->lock);
+	for (i=0, bit=1; i < V4L2_CHN_EV_COUNT; i++, bit<<=1) {
+		if (fh->chn_ev_idx[i] != dev->chn_ev_idx[i]) {
+			fh->chn_ev_idx[i] = dev->chn_ev_idx[i];
+			ev_mask |= bit;
+		}
+	}
+	up(&dev->lock);
+
+	return ev_mask;
+}
+
+/* ----------------------------------------------------------------------- */
+/* user priority management                                                */
+
+static
+int check_alloc_prio(struct saa7134_fh *fh, int req_prio)
+{
+	struct saa7134_dev *dev = fh->dev;
+
+	down(&dev->lock);
+	if ( (req_prio == V4L2_CHN_PRIO_EXCL) && (dev->user_prios[V4L2_CHN_PRIO_EXCL] != 0) ) {
+		/* only one client is allowed at exclusive level */
+		up(&dev->lock);
+		return 0;
+	}
+
+	if (fh->user_prio < V4L2_CHN_PRIO_COUNT) {
+		/* deregister the previous priority */
+		if (dev->user_prios[fh->user_prio] != 0) {
+			dev->user_prios[fh->user_prio] -= 1;
+		} else {
+			printk("saa7134: BUG! (alloc_prio)\n");
+		}
+        }
+
+	/* register the new priority */
+	dev->user_prios[req_prio] += 1;
+	fh->user_prio = req_prio;
+
+	post_chn_event(dev, V4L2_CHN_EV_PRIO);
+
+	up(&dev->lock);
+	return 1;
+}
+
+static
+void release_prio(struct saa7134_dev *dev, struct saa7134_fh *fh)
+{
+	down(&dev->lock);
+	if (dev->user_prios[fh->user_prio] != 0) {
+		dev->user_prios[fh->user_prio] -= 1;
+	} else {
+		printk("saa7134: BUG! (release_prio)\n");
+	}
+	fh->user_prio = 0;
+
+	post_chn_event(dev, V4L2_CHN_EV_PRIO);
+
+	up(&dev->lock);
+}
+
+static
+int get_max_user_prio(struct saa7134_dev *dev)
+{
+	int idx;
+
+	for (idx = V4L2_CHN_PRIO_COUNT - 1; idx > 0; idx--)
+		if (dev->user_prios[idx] > 0)
+			return idx;
+
+	return 0;
+}
+
+static
+int check_prio(struct saa7134_dev *dev, struct semaphore * p_lock,
+               int user_prio, enum v4l2_chn_event_bit ev)
+{
+	int idx;
+
+	/* note: mutex must be locked when this function is called and
+	** will be unlocked if the function returns FALSE */
+
+	for (idx = user_prio + 1; idx < V4L2_CHN_PRIO_COUNT; idx++) {
+		if (dev->user_prios[idx] > 0) {
+			up(p_lock);
+			return 0;
+		}
+	}
+
+	post_chn_event(dev, ev);
+
+	return 1;
+}
+
 /* ------------------------------------------------------------------ */
 
 static void set_tvnorm(struct saa7134_dev *dev, struct saa7134_tvnorm *norm)
@@ -1170,6 +1316,10 @@
 	fh->width    = 768;
 	fh->height   = 576;
 
+	fh->chn_ev_poll = 0;
+	fh->user_prio = V4L2_CHN_PRIO_COUNT;
+	check_alloc_prio(fh, V4L2_CHN_PRIO_DEFAULT);
+
 	videobuf_queue_init(&fh->cap, &video_qops,
 			    dev->pci, &dev->slock,
 			    V4L2_BUF_TYPE_VIDEO_CAPTURE,
@@ -1205,6 +1355,9 @@
 {
 	struct saa7134_fh *fh = file->private_data;
 
+	if (fh->chn_ev_poll)
+		return -EBUSY;
+
 	switch (fh->type) {
 	case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 		if (res_locked(fh->dev,RESOURCE_VIDEO))
@@ -1229,6 +1382,14 @@
 	struct saa7134_fh *fh = file->private_data;
 	struct videobuf_buffer *buf = NULL;
 
+	if (fh->chn_ev_poll) {
+		poll_wait(file, &fh->dev->chn_ev_q, wait);
+		if (memcmp(fh->dev->chn_ev_idx, fh->chn_ev_idx,
+		           sizeof(fh->chn_ev_idx)) != 0)
+			return POLLIN|POLLRDNORM;
+		return 0;
+	}
+
 	if (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type)
 		return videobuf_poll_stream(file, &fh->vbi, wait);
 
@@ -1297,6 +1458,9 @@
 		res_free(dev,fh,RESOURCE_VBI);
 	}
 
+	/* release priority */
+	release_prio(dev, fh);
+
 	saa7134_pgtable_free(dev->pci,&fh->pt_cap);
 	saa7134_pgtable_free(dev->pci,&fh->pt_vbi);
 	
@@ -1550,6 +1714,8 @@
 			return -EINVAL;
 
 		down(&dev->lock);
+		if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_STD))
+			return -EBUSY;
 		if (res_check(fh, RESOURCE_OVERLAY)) {
 			spin_lock_irqsave(&dev->slock,flags);
 			stop_preview(dev,fh);
@@ -1610,6 +1776,8 @@
 		if (NULL == card_in(dev,*i).name)
 			return -EINVAL;
 		down(&dev->lock);
+		if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_INP))
+			return -EBUSY;
 		video_mux(dev,*i);
 		up(&dev->lock);
 		return 0;
@@ -1674,6 +1842,8 @@
 		if (V4L2_TUNER_ANALOG_TV != f->type)
 			return -EINVAL;
 		down(&dev->lock);
+		if (!check_prio(dev, &dev->lock, fh->user_prio, V4L2_CHN_EV_INP))
+			return -EBUSY;
 		dev->ctl_freq = f->frequency;
 		saa7134_i2c_call_clients(dev,VIDIOCSFREQ,&dev->ctl_freq);
 		up(&dev->lock);
@@ -1795,6 +1965,8 @@
 		if (*on) {
 			if (!res_get(dev,fh,RESOURCE_OVERLAY))
 				return -EBUSY;
+			if (fh->chn_ev_poll)
+				return -EBUSY;
 			spin_lock_irqsave(&dev->slock,flags);
 			start_preview(dev,fh);
 			spin_unlock_irqrestore(&dev->slock,flags);
@@ -1866,6 +2038,8 @@
 	{
 		int res = saa7134_resource(fh);
 
+		if (fh->chn_ev_poll)
+			return -EBUSY;
                 if (!res_get(dev,fh,res))
 			return -EBUSY;
 		return videobuf_streamon(file,saa7134_queue(fh));
@@ -1879,6 +2053,51 @@
 			return err;
 		res_free(dev,fh,res);
 		return 0;
+	}
+
+	/* --- preliminary ------------------------------------------- */
+        case VIDIOC_G_CHNPRIO:
+        {
+                enum v4l2_chn_prio *chn_prio = arg;
+		*chn_prio = get_max_user_prio(dev);
+                return 0;
+        }
+        case VIDIOC_S_CHNPRIO:
+        {
+		enum v4l2_chn_prio *chn_prio = arg;
+
+		if ((*chn_prio >= V4L2_CHN_PRIO_COUNT) || (*chn_prio < 0))
+			return -EINVAL;
+		if (!check_alloc_prio(fh, *chn_prio))
+			return -EBUSY;
+		return 0;
+        }
+	case VIDIOC_S_CHNEVENT:
+	{
+		v4l2_chn_event_mask *ev_poll = arg;
+
+		if ((*ev_poll & ~V4L2_CHN_EV_MASK_ALL) != 0)
+			return -EINVAL;
+		if (res_check(fh, RESOURCE_OVERLAY | RESOURCE_VIDEO | RESOURCE_VBI))
+			return -EBUSY;
+
+		fh->chn_ev_poll = (*ev_poll ? 1 : 0);
+		if (fh->chn_ev_poll)
+			get_chn_events(dev, fh);
+		return 0;
+	}
+	case VIDIOC_G_CHNEVENT:
+	{
+		v4l2_chn_event_mask *ev_mask = arg;
+		int retval = 0;
+
+		if (fh->chn_ev_poll == 0)
+			return -EINVAL;
+
+		retval = wait_chn_event(dev, fh, file->f_flags & O_NONBLOCK);
+		if (retval == 0)
+			*ev_mask = get_chn_events(dev, fh);
+		return retval;
 	}
 
 	default:
diff -u orig/saa7134.h ./saa7134.h
--- orig/saa7134.h	Mon Jun  2 12:40:46 2003
+++ ./saa7134.h	Sun Jun 15 12:53:43 2003
@@ -84,6 +84,25 @@
 	CCIR656 = 1,
 };
 
+/* XXX note: the following should go into videodev2.h */
+enum v4l2_chn_prio {
+	V4L2_CHN_PRIO_BACKGROUND,
+	V4L2_CHN_PRIO_INTERACTIVE,
+	V4L2_CHN_PRIO_DEFAULT = V4L2_CHN_PRIO_INTERACTIVE,
+	V4L2_CHN_PRIO_RADIO   = V4L2_CHN_PRIO_INTERACTIVE,
+	V4L2_CHN_PRIO_RECORD,
+	V4L2_CHN_PRIO_EXCL,
+	V4L2_CHN_PRIO_COUNT
+};
+enum v4l2_chn_event_bit {
+        V4L2_CHN_EV_STD,
+        V4L2_CHN_EV_INP,
+        V4L2_CHN_EV_PRIO,
+        V4L2_CHN_EV_COUNT
+};
+typedef __u32 v4l2_chn_event_mask;
+#define V4L2_CHN_EV_MASK_ALL  ((1 << V4L2_CHN_EV_COUNT) - 1)
+
 /* ----------------------------------------------------------- */
 /* static data                                                 */
 
@@ -257,6 +276,9 @@
 	struct v4l2_clip           clips[8];
 	unsigned int               nclips;
 	unsigned int               resources;
+	int                        user_prio;
+	unsigned char              chn_ev_idx[V4L2_CHN_EV_COUNT];
+	int                        chn_ev_poll;
 
 	/* video capture */
 	struct saa7134_format      *fmt;
@@ -315,6 +337,9 @@
 
 	/* various device info */
 	unsigned int               resources;
+	int                        user_prios[V4L2_CHN_PRIO_COUNT];
+	wait_queue_head_t          chn_ev_q;
+	unsigned char              chn_ev_idx[V4L2_CHN_EV_COUNT];
 	struct video_device        video_dev;
 	struct video_device        ts_dev;
 	struct video_device        radio_dev;
@@ -375,6 +400,15 @@
 	unsigned int               hw_mute;
 	int                        last_carrier;
 };
+
+/* ----------------------------------------------------------- */
+/* private ioctls */
+
+/* XXX note: the following should go into videodev2.h */
+#define VIDIOC_G_CHNPRIO        _IOR('V' , BASE_VIDIOCPRIVATE+10, enum v4l2_chn_prio)
+#define VIDIOC_S_CHNPRIO        _IOW('V' , BASE_VIDIOCPRIVATE+11, enum v4l2_chn_prio)
+#define VIDIOC_G_CHNEVENT       _IOR('V' , BASE_VIDIOCPRIVATE+12, v4l2_chn_event_mask)
+#define VIDIOC_S_CHNEVENT       _IOW('V' , BASE_VIDIOCPRIVATE+13, v4l2_chn_event_mask)
 
 /* ----------------------------------------------------------- */