[PATCH v2 3/3] saa7134: add notification about TV standard changes

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The v4l2 event of type V4L2_EVENT_SIGNALCHANGED is emitted
when the current TV standard changes.

Signed-off-by: Mikhail Domrachev <mihail.domrychev@xxxxxxxxx>
---
 drivers/media/pci/saa7134/saa7134-video.c | 125 +++++++++++++++++++++++++++++-
 drivers/media/pci/saa7134/saa7134.h       |  11 +++
 2 files changed, 135 insertions(+), 1 deletion(-)

diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c
index bc85d84..0c0f218 100644
--- a/drivers/media/pci/saa7134/saa7134-video.c
+++ b/drivers/media/pci/saa7134/saa7134-video.c
@@ -24,6 +24,9 @@
 #include <linux/list.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
 
@@ -51,6 +54,7 @@ MODULE_PARM_DESC(noninterlaced,"capture non interlaced video");
 module_param_string(secam, secam, sizeof(secam), 0644);
 MODULE_PARM_DESC(secam, "force SECAM variant, either DK,L or Lc");
 
+static int saa7134_standard_detector_thread(void *arg);
 
 #define dprintk(fmt, arg...)	if (video_debug&0x04) \
 	printk(KERN_DEBUG "%s/video: " fmt, dev->name , ## arg)
@@ -2097,6 +2101,17 @@ static int radio_s_tuner(struct file *file, void *priv,
 	return 0;
 }
 
+static int saa7134_subscribe_event(struct v4l2_fh *fh,
+		const struct v4l2_event_subscription *sub)
+{
+	if (sub->type == V4L2_EVENT_SIGNALCHANGED) {
+		return v4l2_event_subscribe(fh, sub,
+				SAA7134_EVENTS_QUEUE_SIZE, NULL);
+	} else {
+		return v4l2_ctrl_subscribe_event(fh, sub);
+	}
+}
+
 static const struct v4l2_file_operations video_fops =
 {
 	.owner	  = THIS_MODULE,
@@ -2148,7 +2163,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
 	.vidioc_s_register              = vidioc_s_register,
 #endif
 	.vidioc_log_status		= v4l2_ctrl_log_status,
-	.vidioc_subscribe_event		= v4l2_ctrl_subscribe_event,
+	.vidioc_subscribe_event		= saa7134_subscribe_event,
 	.vidioc_unsubscribe_event	= v4l2_event_unsubscribe,
 };
 
@@ -2317,6 +2332,11 @@ int saa7134_video_init1(struct saa7134_dev *dev)
 
 void saa7134_video_fini(struct saa7134_dev *dev)
 {
+	if (dev->std_thread.thread && !dev->std_thread.stopped) {
+		kthread_stop(dev->std_thread.thread);
+		dev->std_thread.thread = NULL;
+	}
+
 	/* free stuff */
 	saa7134_pgtable_free(dev->pci, &dev->pt_cap);
 	saa7134_pgtable_free(dev->pci, &dev->pt_vbi);
@@ -2369,6 +2389,107 @@ int saa7134_video_init2(struct saa7134_dev *dev)
 	v4l2_ctrl_handler_setup(&dev->ctrl_handler);
 	saa7134_tvaudio_setmute(dev);
 	saa7134_tvaudio_setvolume(dev,dev->ctl_volume);
+
+	dev->std_thread.thread = NULL;
+	dev->std_thread.stopped = 0;
+	atomic_set(&dev->std_thread.scan1, 0);
+	atomic_set(&dev->std_thread.scan2, 0);
+
+	dev->std_thread.thread = kthread_run(saa7134_standard_detector_thread,
+			dev, "%s", dev->name);
+	if (IS_ERR(dev->std_thread.thread)) {
+		dev_alert(dev->v4l2_dev.dev, "%s: kthread_run(saa7134_standard_detector_thread) failed\n",
+				dev->name);
+		dev->std_thread.thread = NULL;
+		dev->std_thread.stopped = 1;
+	}
+	return 0;
+}
+
+static const char *saa7134_std_to_str(v4l2_std_id std)
+{
+	switch (std) {
+	case V4L2_STD_NTSC:
+		return "NTSC";
+	case V4L2_STD_PAL:
+		return "PAL";
+	case V4L2_STD_SECAM:
+		return "SECAM";
+	default:
+		return "(no signal)";
+	}
+}
+
+static int saa7134_std_sleep(struct saa7134_dev *dev, int timeout)
+{
+	int cmp = (atomic_read(&dev->std_thread.scan1)
+			== atomic_read(&dev->std_thread.scan2));
+
+	if (cmp && !kthread_should_stop()) {
+		if (timeout < 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule();
+		} else {
+			schedule_timeout_interruptible(
+					msecs_to_jiffies(timeout));
+		}
+	}
+	cmp = (atomic_read(&dev->std_thread.scan1)
+			!= atomic_read(&dev->std_thread.scan2));
+	return cmp;
+}
+
+static int saa7134_standard_detector_thread(void *arg)
+{
+	struct saa7134_dev *dev = arg;
+	v4l2_std_id dcstd = 0;
+	struct v4l2_event event;
+	static const int detect_time = 10; /* msec */
+	static const int max_detect_time = 2000; /* msec */
+	int time_spent = 0;
+
+	set_freezable();
+	for (;;) {
+		saa7134_std_sleep(dev, -1);
+restart:
+		if (kthread_should_stop())
+			goto done;
+		try_to_freeze();
+
+		atomic_set(&dev->std_thread.scan1,
+				atomic_read(&dev->std_thread.scan2));
+
+		if (saa7134_std_sleep(dev, detect_time)) {
+			time_spent = 0;
+			goto restart;
+		}
+		time_spent += detect_time;
+		dcstd = saa7134_read_std(dev);
+		if (dcstd == V4L2_STD_ALL && time_spent < max_detect_time)
+			goto restart;
+
+		dprintk("Standard detected in %d msecs: %s\n", time_spent,
+				saa7134_std_to_str(dcstd));
+		time_spent = 0;
+
+		memset(&event, 0, sizeof(event));
+		event.type = V4L2_EVENT_SIGNALCHANGED;
+		memcpy(event.u.data, &dcstd, sizeof(dcstd));
+		v4l2_event_queue(dev->video_dev, &event);
+	}
+
+done:
+	dev->std_thread.stopped = 1;
+	return 0;
+}
+
+static int saa7134_std_do_scan(struct saa7134_dev *dev)
+{
+	if (dev->std_thread.thread) {
+		atomic_inc(&dev->std_thread.scan2);
+		if (!dev->insuspend && !dev->std_thread.stopped)
+			wake_up_process(dev->std_thread.thread);
+	}
 	return 0;
 }
 
@@ -2403,6 +2524,8 @@ void saa7134_irq_video_signalchange(struct saa7134_dev *dev)
 
 	if (dev->mops && dev->mops->signal_change)
 		dev->mops->signal_change(dev);
+
+	saa7134_std_do_scan(dev);
 }
 
 
diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h
index 9c2249b..2dac74a 100644
--- a/drivers/media/pci/saa7134/saa7134.h
+++ b/drivers/media/pci/saa7134/saa7134.h
@@ -342,6 +342,8 @@ struct saa7134_card_ir {
 #define SAA7134_MAXBOARDS 32
 #define SAA7134_INPUT_MAX 8
 
+#define SAA7134_EVENTS_QUEUE_SIZE 10
+
 /* ----------------------------------------------------------- */
 /* Since we support 2 remote types, lets tell them apart       */
 
@@ -450,6 +452,14 @@ struct saa7134_thread {
 	unsigned int		   stopped;
 };
 
+/* tv standard thread status */
+struct saa7134_thread_std {
+	struct task_struct             *thread;
+	atomic_t                       scan1;
+	atomic_t                       scan2;
+	unsigned int                   stopped;
+};
+
 /* buffer for one video/vbi/ts frame */
 struct saa7134_buf {
 	/* common v4l buffer stuff -- must be first */
@@ -624,6 +634,7 @@ struct saa7134_dev {
 	/* other global state info */
 	unsigned int               automute;
 	struct saa7134_thread      thread;
+	struct saa7134_thread_std  std_thread;
 	struct saa7134_input       *input;
 	struct saa7134_input       *hw_input;
 	unsigned int               hw_mute;
-- 
1.8.5.3



--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux