Message ID | 1395661349.2916.3.camel@localhost.localdomain (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Sorry, I forgot to add the maintainer to the recipients. > saa7134: automatic norm detection switched on > saa7134: vidioc_querystd added > saa7134: notification about TV norm changes via V4L2 event interface added > videodev2: new event type added > > Signed-off-by: Mikhail Domrachev <mihail.domrychev@comexp.ru> > > --- > drivers/media/pci/saa7134/saa7134-empress.c | 1 + > drivers/media/pci/saa7134/saa7134-reg.h | 7 + > drivers/media/pci/saa7134/saa7134-tvaudio.c | 56 +++--- > drivers/media/pci/saa7134/saa7134-video.c | 274 ++++++++++++++++++++++++++-- > drivers/media/pci/saa7134/saa7134.h | 14 +- > include/uapi/linux/videodev2.h | 2 + > 6 files changed, 311 insertions(+), 43 deletions(-) > > diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c > index 0a9047e..a150deb 100644 > --- a/drivers/media/pci/saa7134/saa7134-empress.c > +++ b/drivers/media/pci/saa7134/saa7134-empress.c > @@ -262,6 +262,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { > .vidioc_s_input = saa7134_s_input, > .vidioc_s_std = saa7134_s_std, > .vidioc_g_std = saa7134_g_std, > + .vidioc_querystd = saa7134_querystd, > .vidioc_log_status = v4l2_ctrl_log_status, > .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h > index e7e0af1..d3be05c 100644 > --- a/drivers/media/pci/saa7134/saa7134-reg.h > +++ b/drivers/media/pci/saa7134/saa7134-reg.h > @@ -369,6 +369,13 @@ > #define SAA7135_DSP_RWCLEAR_RERR 1 > > #define SAA7133_I2S_AUDIO_CONTROL 0x591 > + > +#define SAA7134_STDDETECT_AUFD (1 << 7) > +#define SAA7134_STDDETECT_FCTC (1 << 2) > +#define SAA7134_STDDETECT_LDEL (1 << 5) > +#define SAA7134_STDDETECT_AUTO0 (1 << 1) > +#define SAA7134_STDDETECT_AUTO1 (1 << 2) > + > /* ------------------------------------------------------------------ */ > /* > * Local variables: > diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c > index 0f34e09..6380e49 100644 > --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c > +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c > @@ -315,7 +315,7 @@ static void tvaudio_setmode(struct saa7134_dev *dev, > > static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > { > - if (dev->thread.scan1 == dev->thread.scan2 && > + if (dev->audio_thread.scan1 == dev->audio_thread.scan2 && > !kthread_should_stop()) { > if (timeout < 0) { > set_current_state(TASK_INTERRUPTIBLE); > @@ -325,7 +325,7 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > (msecs_to_jiffies(timeout)); > } > } > - return dev->thread.scan1 != dev->thread.scan2; > + return dev->audio_thread.scan1 != dev->audio_thread.scan2; > } > > static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) > @@ -488,8 +488,8 @@ static int tvaudio_thread(void *data) > restart: > try_to_freeze(); > > - dev->thread.scan1 = dev->thread.scan2; > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > dev->tvaudio = NULL; > > saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); > @@ -528,7 +528,7 @@ static int tvaudio_thread(void *data) > tvaudio_setmode(dev,&tvaudio[0],NULL); > for (i = 0; i < ARRAY_SIZE(mainscan); i++) { > carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); > - if (dev->thread.scan1 != dev->thread.scan2) > + if (dev->audio_thread.scan1 != dev->audio_thread.scan2) > goto restart; > } > for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { > @@ -604,11 +604,11 @@ static int tvaudio_thread(void *data) > goto restart; > if (kthread_should_stop()) > break; > - if (UNSET == dev->thread.mode) { > + if (UNSET == dev->audio_thread.mode) { > rx = tvaudio_getstereo(dev, &tvaudio[audio]); > mode = saa7134_tvaudio_rx2mode(rx); > } else { > - mode = dev->thread.mode; > + mode = dev->audio_thread.mode; > } > if (lastmode != mode) { > tvaudio_setstereo(dev,&tvaudio[audio],mode); > @@ -618,7 +618,7 @@ static int tvaudio_thread(void *data) > } > > done: > - dev->thread.stopped = 1; > + dev->audio_thread.stopped = 1; > return 0; > } > > @@ -785,8 +785,8 @@ static int tvaudio_thread_ddep(void *data) > restart: > try_to_freeze(); > > - dev->thread.scan1 = dev->thread.scan2; > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > > if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { > /* insmod option override */ > @@ -803,16 +803,18 @@ static int tvaudio_thread_ddep(void *data) > } > } else { > /* (let chip) scan for sound carrier */ > + v4l2_std_id id = dev->tvnorm->id; > norms = 0; > - if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) > + > + if (id & (V4L2_STD_B | V4L2_STD_GH)) > norms |= 0x04; > - if (dev->tvnorm->id & V4L2_STD_PAL_I) > + if (id & V4L2_STD_PAL_I) > norms |= 0x20; > - if (dev->tvnorm->id & V4L2_STD_DK) > + if (id & V4L2_STD_DK) > norms |= 0x08; > - if (dev->tvnorm->id & V4L2_STD_MN) > + if (id & V4L2_STD_MN) > norms |= 0x40; > - if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > + if (id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > norms |= 0x10; > if (0 == norms) > norms = 0x7c; /* all */ > @@ -862,7 +864,7 @@ static int tvaudio_thread_ddep(void *data) > } > > done: > - dev->thread.stopped = 1; > + dev->audio_thread.stopped = 1; > return 0; > } > > @@ -1024,13 +1026,13 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) > break; > } > > - dev->thread.thread = NULL; > - dev->thread.scan1 = dev->thread.scan2 = 0; > + dev->audio_thread.thread = NULL; > + dev->audio_thread.scan1 = dev->audio_thread.scan2 = 0; > if (my_thread) { > saa7134_tvaudio_init(dev); > /* start tvaudio thread */ > - dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > - if (IS_ERR(dev->thread.thread)) { > + dev->audio_thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > + if (IS_ERR(dev->audio_thread.thread)) { > printk(KERN_WARNING "%s: kernel_thread() failed\n", > dev->name); > /* XXX: missing error handling here */ > @@ -1051,8 +1053,8 @@ int saa7134_tvaudio_close(struct saa7134_dev *dev) > int saa7134_tvaudio_fini(struct saa7134_dev *dev) > { > /* shutdown tvaudio thread */ > - if (dev->thread.thread && !dev->thread.stopped) > - kthread_stop(dev->thread.thread); > + if (dev->audio_thread.thread && !dev->audio_thread.stopped) > + kthread_stop(dev->audio_thread.thread); > > saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ > return 0; > @@ -1064,12 +1066,12 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) > dprintk("sound IF not in use, skipping scan\n"); > dev->automute = 0; > saa7134_tvaudio_setmute(dev); > - } else if (dev->thread.thread) { > - dev->thread.mode = UNSET; > - dev->thread.scan2++; > + } else if (dev->audio_thread.thread) { > + dev->audio_thread.mode = UNSET; > + dev->audio_thread.scan2++; > > - if (!dev->insuspend && !dev->thread.stopped) > - wake_up_process(dev->thread.thread); > + if (!dev->insuspend && !dev->audio_thread.stopped) > + wake_up_process(dev->audio_thread.thread); > } else { > dev->automute = 0; > saa7134_tvaudio_setmute(dev); > diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c > index eb472b5..2528b6d 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> > > @@ -452,19 +455,28 @@ static void video_mux(struct saa7134_dev *dev, int input) > > static void saa7134_set_decoder(struct saa7134_dev *dev) > { > - int luma_control, sync_control, mux; > + int luma_control, sync_control, chroma_ctrl1, analog_adc, vgate_misc, mux; > > struct saa7134_tvnorm *norm = dev->tvnorm; > mux = card_in(dev, dev->ctl_input).vmux; > > luma_control = norm->luma_control; > sync_control = norm->sync_control; > + chroma_ctrl1 = norm->chroma_ctrl1; > + analog_adc = 0x01; > + vgate_misc = norm->vgate_misc; > > if (mux > 5) > luma_control |= 0x80; /* svideo */ > if (noninterlaced || dev->nosignal) > sync_control |= 0x20; > > + /* switch on auto standard detection */ > + sync_control |= SAA7134_STDDETECT_AUFD; > + chroma_ctrl1 |= SAA7134_STDDETECT_AUTO0; > + chroma_ctrl1 &= ~SAA7134_STDDETECT_FCTC; > + luma_control &= ~SAA7134_STDDETECT_LDEL; > + > /* setup video decoder */ > saa_writeb(SAA7134_INCR_DELAY, 0x08); > saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); > @@ -487,16 +499,16 @@ static void saa7134_set_decoder(struct saa7134_dev *dev) > dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); > > saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); > - saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); > + saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1); > saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); > > saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); > saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); > > - saa_writeb(SAA7134_ANALOG_ADC, 0x01); > + saa_writeb(SAA7134_ANALOG_ADC, analog_adc); > saa_writeb(SAA7134_VGATE_START, 0x11); > saa_writeb(SAA7134_VGATE_STOP, 0xfe); > - saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); > + saa_writeb(SAA7134_MISC_VGATE_MSB, vgate_misc); > saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); > saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); > } > @@ -1686,6 +1698,98 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) > } > EXPORT_SYMBOL_GPL(saa7134_g_std); > > +static v4l2_std_id saa7134_read_std(struct saa7134_dev* dev) > +{ > + static v4l2_std_id stds[] = { V4L2_STD_ALL, V4L2_STD_NTSC, V4L2_STD_PAL, V4L2_STD_SECAM }; > + v4l2_std_id result = 0; > + > + u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1); > + u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2); > + > + if (!(st2 & 0x1)) //RDCAP == 0 > + result = V4L2_STD_ALL; > + else > + result = stds[st1 & 0x03]; > + > + return result; > +} > + > +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)"; > + } > +} > + > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std) > +{ > + struct saa7134_dev *dev = video_drvdata(file); > + > + v4l2_std_id dcstd = saa7134_read_std(dev); > + if (dcstd != V4L2_STD_ALL) > + *std &= dcstd; > + else > + *std = dcstd; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(saa7134_querystd); > + > +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; > + > + set_freezable(); > + for (;;) { > + saa7134_std_sleep(dev,-1); > + if (kthread_should_stop()) > + goto done; > +restart: > + try_to_freeze(); > + > + atomic_set(&dev->std_thread.scan1, atomic_read(&dev->std_thread.scan2)); > + > + if (saa7134_std_sleep(dev,3000)) > + goto restart; > + dcstd = saa7134_read_std(dev); > + > + dprintk("Standard detected: %s", saa7134_std_to_str(dcstd)); > + 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_cropcap(struct file *file, void *priv, > struct v4l2_cropcap *cap) > { > @@ -1793,13 +1897,13 @@ int saa7134_s_tuner(struct file *file, void *priv, > if (0 != t->index) > return -EINVAL; > > - mode = dev->thread.mode; > + mode = dev->audio_thread.mode; > if (UNSET == mode) { > rx = saa7134_tvaudio_getstereo(dev); > mode = saa7134_tvaudio_rx2mode(rx); > } > if (mode != t->audmode) > - dev->thread.mode = t->audmode; > + dev->audio_thread.mode = t->audmode; > > return 0; > } > @@ -2053,6 +2157,18 @@ 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, > @@ -2084,6 +2200,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { > .vidioc_dqbuf = saa7134_dqbuf, > .vidioc_s_std = saa7134_s_std, > .vidioc_g_std = saa7134_g_std, > + .vidioc_querystd = saa7134_querystd, > .vidioc_enum_input = saa7134_enum_input, > .vidioc_g_input = saa7134_g_input, > .vidioc_s_input = saa7134_s_input, > @@ -2103,7 +2220,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, > }; > > @@ -2272,6 +2389,9 @@ 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); > + > /* free stuff */ > saa7134_pgtable_free(dev->pci, &dev->pt_cap); > saa7134_pgtable_free(dev->pci, &dev->pt_vbi); > @@ -2324,23 +2444,144 @@ 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)) { > + printk(KERN_ALERT "%s: kthread_run(saa7134_standard_detector_thread) failed\n", dev->name); > + dev->std_thread.thread = NULL; > + 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; > } > > void saa7134_irq_video_signalchange(struct saa7134_dev *dev) > { > - static const char *st[] = { > - "(no signal)", "NTSC", "PAL", "SECAM" }; > - u32 st1,st2; > + static const char* st1_6_hlck[] = > + { > + [0] = "HPLL is locked; clock LLC is locked to line frequency", > + [1] = "HPLL is not locked" > + }; > + static const char* st1_5_sltca[] = > + { > + [0] = "Video AGC is in normal operation mode", > + [1] = "Video AGC is in gain recover mode (increase) after peak attack" > + }; > + static const char* st1_4_glimt[] = > + { > + [0] = "Video AGC is in normal operation mode", > + [1] = "Video AGC is on its top limit, i.e. input signal is too large, out of AGC range" > + }; > + static const char* st1_3_glimb[] = > + { > + [0] = "video AGC is in normal operation mode", > + [1] = "video AGC is on its bottom limit. Input signal is too small, out of AGC range" > + }; > + static const char* st1_2_wipa[] = > + { > + [0] = "White or Color Peak Control loop is not activated", > + [1] = "White or Color Peak Control was triggered in previous line" > + }; > + struct st1_1_0_dcstd_descr > + { > + const char* name; > + v4l2_std_id stdid; > + }; > + static struct st1_1_0_dcstd_descr st1_1_0_dcstd[] = > + { > + [0] = { "No color signal could be detected. The video is assumed being B/W", V4L2_STD_UNKNOWN}, > + [1] = { "NTSC signal detected", V4L2_STD_NTSC}, > + [2] = { "PAL signal detected", V4L2_STD_PAL}, > + [3] = { "SECAM signal detected", V4L2_STD_SECAM} > + }; > + static const char* st2_7_intl[] = > + { > + [0] = "Video input is detected as non-interlaced", > + [1] = "Video input is detected as interlaced" > + }; > + static const char* st2_6_hlvln[] = > + { > + [0] = "Horizontal and vertical synchronization is achieved", > + [1] = "Either horizontal or vertical synchronization is lost" > + }; > + static const char* st2_5_fidt[] = > + { > + [0] = "Video input is detected with 50 Hz field rate", > + [1] = "Video input is detected with 60 Hz field rate" > + }; > + static const char* st2_3_type3[] = > + { > + [0] = "normal video signal", > + [1] = "Macrovision copy protection type 3 detected" > + }; > + static const char* st2_2_colstr[] = > + { > + [0] = "normal video signal", > + [1] = "Macrovision Color Stripe scheme detected" > + }; > + static const char* st2_1_copro[] = > + { > + [0] = "normal video signal, No Macrovision copy protection scheme detected", > + [1] = "Copy protection detected according Macrovision (including 7.01)" > + }; > + static const char* st2_0_rdcap[] = > + { > + [0] = "One or more of the decoder control loops are not locked", > + [1] = "Ready for capture. All control loops are locked: Horizontal, Vertical, color subcarrier, chroma gain control" > + }; > + > + u8 st1,st2; > > st1 = saa_readb(SAA7134_STATUS_VIDEO1); > st2 = saa_readb(SAA7134_STATUS_VIDEO2); > - dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", > - (st1 & 0x40) ? "not locked" : "locked", > - (st2 & 0x40) ? "no" : "yes", > - st[st1 & 0x03]); > - dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > > + dprintk("saa7134: DEBUG. Status byte 1:\n" > + "HLCK = %s\n" > + "SLTCA = %s\n" > + "GLIMT = %s\n" > + "GLIMB = %s\n" > + "WIPA = %s\n" > + "DCSTD = %s\n", > + st1_6_hlck[((st1 & (1 << 6)) ? 1 : 0)], > + st1_5_sltca[((st1 & (1 << 5)) ? 1 : 0)], > + st1_4_glimt[((st1 & (1 << 4)) ? 1 : 0)], > + st1_3_glimb[((st1 & (1 << 3)) ? 1 : 0)], > + st1_2_wipa[((st1 & (1 << 2)) ? 1 : 0)], > + st1_1_0_dcstd[st1 & 0x03].name > + ); > + dprintk("saa7134: DEBUG. Status byte 2:\n" > + "INTL = %s\n" > + "HLVLN = %s\n" > + "FIDT = %s\n" > + "TYPE3 = %s\n" > + "COLSTR = %s\n" > + "COPRO = %s\n" > + "RDCAP = %s\n", > + st2_7_intl[((st2 & (1 << 7)) ? 1 : 0)], > + st2_6_hlvln[((st2 & (1 << 6)) ? 1 : 0)], > + st2_5_fidt[((st2 & (1 << 5)) ? 1 : 0)], > + st2_3_type3[((st2 & (1 << 3)) ? 1 : 0)], > + st2_2_colstr[((st2 & (1 << 2)) ? 1 : 0)], > + st2_1_copro[((st2 & (1 << 1)) ? 1 : 0)], > + st2_0_rdcap[((st2 & (1 << 0)) ? 1 : 0)] > + ); > + > + dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > if (dev->nosignal) { > /* no video signal -> mute audio */ > if (dev->ctl_automute) > @@ -2358,6 +2599,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); > } > > > @@ -2393,6 +2636,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) > spin_unlock(&dev->slock); > } > > + > /* ----------------------------------------------------------- */ > /* > * Local variables: > diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h > index 2474e84..0e5bd3d 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 */ > @@ -623,7 +633,8 @@ struct saa7134_dev { > > /* other global state info */ > unsigned int automute; > - struct saa7134_thread thread; > + struct saa7134_thread audio_thread; > + struct saa7134_thread_std std_thread; > struct saa7134_input *input; > struct saa7134_input *hw_input; > unsigned int hw_mute; > @@ -779,6 +790,7 @@ extern struct video_device saa7134_radio_template; > > int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id); > int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std); > int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); > int saa7134_g_input(struct file *file, void *priv, unsigned int *i); > int saa7134_s_input(struct file *file, void *priv, unsigned int i); > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h > index e35ad6c..806057b 100644 > --- a/include/uapi/linux/videodev2.h > +++ b/include/uapi/linux/videodev2.h > @@ -1765,8 +1765,10 @@ struct v4l2_streamparm { > #define V4L2_EVENT_EOS 2 > #define V4L2_EVENT_CTRL 3 > #define V4L2_EVENT_FRAME_SYNC 4 > +#define V4L2_EVENT_SIGNALCHANGED 5 > #define V4L2_EVENT_PRIVATE_START 0x08000000 > > + > /* Payload for V4L2_EVENT_VSYNC */ > struct v4l2_event_vsync { > /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */ -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Mikhail, Thank you for the patch. However, it does need some work before I can accept it. First of all, run your patch through scripts/checkpatch.pl to ensure it complies to the kernel coding style. Secondly, split up this single patch in smaller ones: in particular the addition of the new event type needs to be in a patch of its own. Thirdly, you need to document the new event type in the DocBook documentation as well. API additions are only accepted if the documentation is updated at the same time. I also wonder why you need a thread to watch for signal changes. It's not wrong, but in practice a TV input signal rarely if ever changes format. It can be different between different countries or when testing with a signal generator, but the normal case is that you are just interested in the current standard, and not how it might change over time. That would simplify the code a lot. This is what other drivers that implement querystd do. Regards, Hans On 03/24/2014 12:42 PM, Mikhail Domrachev wrote: > saa7134: automatic norm detection switched on > saa7134: vidioc_querystd added > saa7134: notification about TV norm changes via V4L2 event interface added > videodev2: new event type added > > Signed-off-by: Mikhail Domrachev <mihail.domrychev@comexp.ru> > > --- > drivers/media/pci/saa7134/saa7134-empress.c | 1 + > drivers/media/pci/saa7134/saa7134-reg.h | 7 + > drivers/media/pci/saa7134/saa7134-tvaudio.c | 56 +++--- > drivers/media/pci/saa7134/saa7134-video.c | 274 ++++++++++++++++++++++++++-- > drivers/media/pci/saa7134/saa7134.h | 14 +- > include/uapi/linux/videodev2.h | 2 + > 6 files changed, 311 insertions(+), 43 deletions(-) > > diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c > index 0a9047e..a150deb 100644 > --- a/drivers/media/pci/saa7134/saa7134-empress.c > +++ b/drivers/media/pci/saa7134/saa7134-empress.c > @@ -262,6 +262,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { > .vidioc_s_input = saa7134_s_input, > .vidioc_s_std = saa7134_s_std, > .vidioc_g_std = saa7134_g_std, > + .vidioc_querystd = saa7134_querystd, > .vidioc_log_status = v4l2_ctrl_log_status, > .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h > index e7e0af1..d3be05c 100644 > --- a/drivers/media/pci/saa7134/saa7134-reg.h > +++ b/drivers/media/pci/saa7134/saa7134-reg.h > @@ -369,6 +369,13 @@ > #define SAA7135_DSP_RWCLEAR_RERR 1 > > #define SAA7133_I2S_AUDIO_CONTROL 0x591 > + > +#define SAA7134_STDDETECT_AUFD (1 << 7) > +#define SAA7134_STDDETECT_FCTC (1 << 2) > +#define SAA7134_STDDETECT_LDEL (1 << 5) > +#define SAA7134_STDDETECT_AUTO0 (1 << 1) > +#define SAA7134_STDDETECT_AUTO1 (1 << 2) > + > /* ------------------------------------------------------------------ */ > /* > * Local variables: > diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c > index 0f34e09..6380e49 100644 > --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c > +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c > @@ -315,7 +315,7 @@ static void tvaudio_setmode(struct saa7134_dev *dev, > > static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > { > - if (dev->thread.scan1 == dev->thread.scan2 && > + if (dev->audio_thread.scan1 == dev->audio_thread.scan2 && > !kthread_should_stop()) { > if (timeout < 0) { > set_current_state(TASK_INTERRUPTIBLE); > @@ -325,7 +325,7 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > (msecs_to_jiffies(timeout)); > } > } > - return dev->thread.scan1 != dev->thread.scan2; > + return dev->audio_thread.scan1 != dev->audio_thread.scan2; > } > > static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) > @@ -488,8 +488,8 @@ static int tvaudio_thread(void *data) > restart: > try_to_freeze(); > > - dev->thread.scan1 = dev->thread.scan2; > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > dev->tvaudio = NULL; > > saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); > @@ -528,7 +528,7 @@ static int tvaudio_thread(void *data) > tvaudio_setmode(dev,&tvaudio[0],NULL); > for (i = 0; i < ARRAY_SIZE(mainscan); i++) { > carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); > - if (dev->thread.scan1 != dev->thread.scan2) > + if (dev->audio_thread.scan1 != dev->audio_thread.scan2) > goto restart; > } > for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { > @@ -604,11 +604,11 @@ static int tvaudio_thread(void *data) > goto restart; > if (kthread_should_stop()) > break; > - if (UNSET == dev->thread.mode) { > + if (UNSET == dev->audio_thread.mode) { > rx = tvaudio_getstereo(dev, &tvaudio[audio]); > mode = saa7134_tvaudio_rx2mode(rx); > } else { > - mode = dev->thread.mode; > + mode = dev->audio_thread.mode; > } > if (lastmode != mode) { > tvaudio_setstereo(dev,&tvaudio[audio],mode); > @@ -618,7 +618,7 @@ static int tvaudio_thread(void *data) > } > > done: > - dev->thread.stopped = 1; > + dev->audio_thread.stopped = 1; > return 0; > } > > @@ -785,8 +785,8 @@ static int tvaudio_thread_ddep(void *data) > restart: > try_to_freeze(); > > - dev->thread.scan1 = dev->thread.scan2; > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > > if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { > /* insmod option override */ > @@ -803,16 +803,18 @@ static int tvaudio_thread_ddep(void *data) > } > } else { > /* (let chip) scan for sound carrier */ > + v4l2_std_id id = dev->tvnorm->id; > norms = 0; > - if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) > + > + if (id & (V4L2_STD_B | V4L2_STD_GH)) > norms |= 0x04; > - if (dev->tvnorm->id & V4L2_STD_PAL_I) > + if (id & V4L2_STD_PAL_I) > norms |= 0x20; > - if (dev->tvnorm->id & V4L2_STD_DK) > + if (id & V4L2_STD_DK) > norms |= 0x08; > - if (dev->tvnorm->id & V4L2_STD_MN) > + if (id & V4L2_STD_MN) > norms |= 0x40; > - if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > + if (id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > norms |= 0x10; > if (0 == norms) > norms = 0x7c; /* all */ > @@ -862,7 +864,7 @@ static int tvaudio_thread_ddep(void *data) > } > > done: > - dev->thread.stopped = 1; > + dev->audio_thread.stopped = 1; > return 0; > } > > @@ -1024,13 +1026,13 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) > break; > } > > - dev->thread.thread = NULL; > - dev->thread.scan1 = dev->thread.scan2 = 0; > + dev->audio_thread.thread = NULL; > + dev->audio_thread.scan1 = dev->audio_thread.scan2 = 0; > if (my_thread) { > saa7134_tvaudio_init(dev); > /* start tvaudio thread */ > - dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > - if (IS_ERR(dev->thread.thread)) { > + dev->audio_thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > + if (IS_ERR(dev->audio_thread.thread)) { > printk(KERN_WARNING "%s: kernel_thread() failed\n", > dev->name); > /* XXX: missing error handling here */ > @@ -1051,8 +1053,8 @@ int saa7134_tvaudio_close(struct saa7134_dev *dev) > int saa7134_tvaudio_fini(struct saa7134_dev *dev) > { > /* shutdown tvaudio thread */ > - if (dev->thread.thread && !dev->thread.stopped) > - kthread_stop(dev->thread.thread); > + if (dev->audio_thread.thread && !dev->audio_thread.stopped) > + kthread_stop(dev->audio_thread.thread); > > saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ > return 0; > @@ -1064,12 +1066,12 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) > dprintk("sound IF not in use, skipping scan\n"); > dev->automute = 0; > saa7134_tvaudio_setmute(dev); > - } else if (dev->thread.thread) { > - dev->thread.mode = UNSET; > - dev->thread.scan2++; > + } else if (dev->audio_thread.thread) { > + dev->audio_thread.mode = UNSET; > + dev->audio_thread.scan2++; > > - if (!dev->insuspend && !dev->thread.stopped) > - wake_up_process(dev->thread.thread); > + if (!dev->insuspend && !dev->audio_thread.stopped) > + wake_up_process(dev->audio_thread.thread); > } else { > dev->automute = 0; > saa7134_tvaudio_setmute(dev); > diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c > index eb472b5..2528b6d 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> > > @@ -452,19 +455,28 @@ static void video_mux(struct saa7134_dev *dev, int input) > > static void saa7134_set_decoder(struct saa7134_dev *dev) > { > - int luma_control, sync_control, mux; > + int luma_control, sync_control, chroma_ctrl1, analog_adc, vgate_misc, mux; > > struct saa7134_tvnorm *norm = dev->tvnorm; > mux = card_in(dev, dev->ctl_input).vmux; > > luma_control = norm->luma_control; > sync_control = norm->sync_control; > + chroma_ctrl1 = norm->chroma_ctrl1; > + analog_adc = 0x01; > + vgate_misc = norm->vgate_misc; > > if (mux > 5) > luma_control |= 0x80; /* svideo */ > if (noninterlaced || dev->nosignal) > sync_control |= 0x20; > > + /* switch on auto standard detection */ > + sync_control |= SAA7134_STDDETECT_AUFD; > + chroma_ctrl1 |= SAA7134_STDDETECT_AUTO0; > + chroma_ctrl1 &= ~SAA7134_STDDETECT_FCTC; > + luma_control &= ~SAA7134_STDDETECT_LDEL; > + > /* setup video decoder */ > saa_writeb(SAA7134_INCR_DELAY, 0x08); > saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); > @@ -487,16 +499,16 @@ static void saa7134_set_decoder(struct saa7134_dev *dev) > dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); > > saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); > - saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); > + saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1); > saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); > > saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); > saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); > > - saa_writeb(SAA7134_ANALOG_ADC, 0x01); > + saa_writeb(SAA7134_ANALOG_ADC, analog_adc); > saa_writeb(SAA7134_VGATE_START, 0x11); > saa_writeb(SAA7134_VGATE_STOP, 0xfe); > - saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); > + saa_writeb(SAA7134_MISC_VGATE_MSB, vgate_misc); > saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); > saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); > } > @@ -1686,6 +1698,98 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) > } > EXPORT_SYMBOL_GPL(saa7134_g_std); > > +static v4l2_std_id saa7134_read_std(struct saa7134_dev* dev) > +{ > + static v4l2_std_id stds[] = { V4L2_STD_ALL, V4L2_STD_NTSC, V4L2_STD_PAL, V4L2_STD_SECAM }; > + v4l2_std_id result = 0; > + > + u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1); > + u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2); > + > + if (!(st2 & 0x1)) //RDCAP == 0 > + result = V4L2_STD_ALL; > + else > + result = stds[st1 & 0x03]; > + > + return result; > +} > + > +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)"; > + } > +} > + > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std) > +{ > + struct saa7134_dev *dev = video_drvdata(file); > + > + v4l2_std_id dcstd = saa7134_read_std(dev); > + if (dcstd != V4L2_STD_ALL) > + *std &= dcstd; > + else > + *std = dcstd; > + > + return 0; > +} > +EXPORT_SYMBOL_GPL(saa7134_querystd); > + > +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; > + > + set_freezable(); > + for (;;) { > + saa7134_std_sleep(dev,-1); > + if (kthread_should_stop()) > + goto done; > +restart: > + try_to_freeze(); > + > + atomic_set(&dev->std_thread.scan1, atomic_read(&dev->std_thread.scan2)); > + > + if (saa7134_std_sleep(dev,3000)) > + goto restart; > + dcstd = saa7134_read_std(dev); > + > + dprintk("Standard detected: %s", saa7134_std_to_str(dcstd)); > + 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_cropcap(struct file *file, void *priv, > struct v4l2_cropcap *cap) > { > @@ -1793,13 +1897,13 @@ int saa7134_s_tuner(struct file *file, void *priv, > if (0 != t->index) > return -EINVAL; > > - mode = dev->thread.mode; > + mode = dev->audio_thread.mode; > if (UNSET == mode) { > rx = saa7134_tvaudio_getstereo(dev); > mode = saa7134_tvaudio_rx2mode(rx); > } > if (mode != t->audmode) > - dev->thread.mode = t->audmode; > + dev->audio_thread.mode = t->audmode; > > return 0; > } > @@ -2053,6 +2157,18 @@ 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, > @@ -2084,6 +2200,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { > .vidioc_dqbuf = saa7134_dqbuf, > .vidioc_s_std = saa7134_s_std, > .vidioc_g_std = saa7134_g_std, > + .vidioc_querystd = saa7134_querystd, > .vidioc_enum_input = saa7134_enum_input, > .vidioc_g_input = saa7134_g_input, > .vidioc_s_input = saa7134_s_input, > @@ -2103,7 +2220,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, > }; > > @@ -2272,6 +2389,9 @@ 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); > + > /* free stuff */ > saa7134_pgtable_free(dev->pci, &dev->pt_cap); > saa7134_pgtable_free(dev->pci, &dev->pt_vbi); > @@ -2324,23 +2444,144 @@ 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)) { > + printk(KERN_ALERT "%s: kthread_run(saa7134_standard_detector_thread) failed\n", dev->name); > + dev->std_thread.thread = NULL; > + 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; > } > > void saa7134_irq_video_signalchange(struct saa7134_dev *dev) > { > - static const char *st[] = { > - "(no signal)", "NTSC", "PAL", "SECAM" }; > - u32 st1,st2; > + static const char* st1_6_hlck[] = > + { > + [0] = "HPLL is locked; clock LLC is locked to line frequency", > + [1] = "HPLL is not locked" > + }; > + static const char* st1_5_sltca[] = > + { > + [0] = "Video AGC is in normal operation mode", > + [1] = "Video AGC is in gain recover mode (increase) after peak attack" > + }; > + static const char* st1_4_glimt[] = > + { > + [0] = "Video AGC is in normal operation mode", > + [1] = "Video AGC is on its top limit, i.e. input signal is too large, out of AGC range" > + }; > + static const char* st1_3_glimb[] = > + { > + [0] = "video AGC is in normal operation mode", > + [1] = "video AGC is on its bottom limit. Input signal is too small, out of AGC range" > + }; > + static const char* st1_2_wipa[] = > + { > + [0] = "White or Color Peak Control loop is not activated", > + [1] = "White or Color Peak Control was triggered in previous line" > + }; > + struct st1_1_0_dcstd_descr > + { > + const char* name; > + v4l2_std_id stdid; > + }; > + static struct st1_1_0_dcstd_descr st1_1_0_dcstd[] = > + { > + [0] = { "No color signal could be detected. The video is assumed being B/W", V4L2_STD_UNKNOWN}, > + [1] = { "NTSC signal detected", V4L2_STD_NTSC}, > + [2] = { "PAL signal detected", V4L2_STD_PAL}, > + [3] = { "SECAM signal detected", V4L2_STD_SECAM} > + }; > + static const char* st2_7_intl[] = > + { > + [0] = "Video input is detected as non-interlaced", > + [1] = "Video input is detected as interlaced" > + }; > + static const char* st2_6_hlvln[] = > + { > + [0] = "Horizontal and vertical synchronization is achieved", > + [1] = "Either horizontal or vertical synchronization is lost" > + }; > + static const char* st2_5_fidt[] = > + { > + [0] = "Video input is detected with 50 Hz field rate", > + [1] = "Video input is detected with 60 Hz field rate" > + }; > + static const char* st2_3_type3[] = > + { > + [0] = "normal video signal", > + [1] = "Macrovision copy protection type 3 detected" > + }; > + static const char* st2_2_colstr[] = > + { > + [0] = "normal video signal", > + [1] = "Macrovision Color Stripe scheme detected" > + }; > + static const char* st2_1_copro[] = > + { > + [0] = "normal video signal, No Macrovision copy protection scheme detected", > + [1] = "Copy protection detected according Macrovision (including 7.01)" > + }; > + static const char* st2_0_rdcap[] = > + { > + [0] = "One or more of the decoder control loops are not locked", > + [1] = "Ready for capture. All control loops are locked: Horizontal, Vertical, color subcarrier, chroma gain control" > + }; > + > + u8 st1,st2; > > st1 = saa_readb(SAA7134_STATUS_VIDEO1); > st2 = saa_readb(SAA7134_STATUS_VIDEO2); > - dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", > - (st1 & 0x40) ? "not locked" : "locked", > - (st2 & 0x40) ? "no" : "yes", > - st[st1 & 0x03]); > - dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > > + dprintk("saa7134: DEBUG. Status byte 1:\n" > + "HLCK = %s\n" > + "SLTCA = %s\n" > + "GLIMT = %s\n" > + "GLIMB = %s\n" > + "WIPA = %s\n" > + "DCSTD = %s\n", > + st1_6_hlck[((st1 & (1 << 6)) ? 1 : 0)], > + st1_5_sltca[((st1 & (1 << 5)) ? 1 : 0)], > + st1_4_glimt[((st1 & (1 << 4)) ? 1 : 0)], > + st1_3_glimb[((st1 & (1 << 3)) ? 1 : 0)], > + st1_2_wipa[((st1 & (1 << 2)) ? 1 : 0)], > + st1_1_0_dcstd[st1 & 0x03].name > + ); > + dprintk("saa7134: DEBUG. Status byte 2:\n" > + "INTL = %s\n" > + "HLVLN = %s\n" > + "FIDT = %s\n" > + "TYPE3 = %s\n" > + "COLSTR = %s\n" > + "COPRO = %s\n" > + "RDCAP = %s\n", > + st2_7_intl[((st2 & (1 << 7)) ? 1 : 0)], > + st2_6_hlvln[((st2 & (1 << 6)) ? 1 : 0)], > + st2_5_fidt[((st2 & (1 << 5)) ? 1 : 0)], > + st2_3_type3[((st2 & (1 << 3)) ? 1 : 0)], > + st2_2_colstr[((st2 & (1 << 2)) ? 1 : 0)], > + st2_1_copro[((st2 & (1 << 1)) ? 1 : 0)], > + st2_0_rdcap[((st2 & (1 << 0)) ? 1 : 0)] > + ); > + > + dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > if (dev->nosignal) { > /* no video signal -> mute audio */ > if (dev->ctl_automute) > @@ -2358,6 +2599,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); > } > > > @@ -2393,6 +2636,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) > spin_unlock(&dev->slock); > } > > + > /* ----------------------------------------------------------- */ > /* > * Local variables: > diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h > index 2474e84..0e5bd3d 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 */ > @@ -623,7 +633,8 @@ struct saa7134_dev { > > /* other global state info */ > unsigned int automute; > - struct saa7134_thread thread; > + struct saa7134_thread audio_thread; > + struct saa7134_thread_std std_thread; > struct saa7134_input *input; > struct saa7134_input *hw_input; > unsigned int hw_mute; > @@ -779,6 +790,7 @@ extern struct video_device saa7134_radio_template; > > int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id); > int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std); > int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); > int saa7134_g_input(struct file *file, void *priv, unsigned int *i); > int saa7134_s_input(struct file *file, void *priv, unsigned int i); > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h > index e35ad6c..806057b 100644 > --- a/include/uapi/linux/videodev2.h > +++ b/include/uapi/linux/videodev2.h > @@ -1765,8 +1765,10 @@ struct v4l2_streamparm { > #define V4L2_EVENT_EOS 2 > #define V4L2_EVENT_CTRL 3 > #define V4L2_EVENT_FRAME_SYNC 4 > +#define V4L2_EVENT_SIGNALCHANGED 5 > #define V4L2_EVENT_PRIVATE_START 0x08000000 > > + > /* Payload for V4L2_EVENT_VSYNC */ > struct v4l2_event_vsync { > /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */ > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Hans, Thank you for comments, I will rework the patch and document the new event type. Let me explain why I created a new thread. My company is engaged in the monitoring of TV air. All TV channels are recorded 24/7 for further analysis. But some local TV channels change the standard over time (SECAM->PAL, PAL->SECAM). So the recording software must be notified about these changes to set a new standard and record the picture but not the noise. Regards, Mikhail On Fri, 2014-03-28 at 09:37 +0100, Hans Verkuil wrote: > Hi Mikhail, > > Thank you for the patch. However, it does need some work before I can accept it. > > First of all, run your patch through scripts/checkpatch.pl to ensure it complies > to the kernel coding style. > > Secondly, split up this single patch in smaller ones: in particular the addition > of the new event type needs to be in a patch of its own. > > Thirdly, you need to document the new event type in the DocBook documentation as > well. API additions are only accepted if the documentation is updated at the same > time. > > I also wonder why you need a thread to watch for signal changes. It's not wrong, > but in practice a TV input signal rarely if ever changes format. It can be different > between different countries or when testing with a signal generator, but the normal > case is that you are just interested in the current standard, and not how it might > change over time. That would simplify the code a lot. This is what other drivers > that implement querystd do. > > Regards, > > Hans > > On 03/24/2014 12:42 PM, Mikhail Domrachev wrote: > > saa7134: automatic norm detection switched on > > saa7134: vidioc_querystd added > > saa7134: notification about TV norm changes via V4L2 event interface added > > videodev2: new event type added > > > > Signed-off-by: Mikhail Domrachev <mihail.domrychev@comexp.ru> > > > > --- > > drivers/media/pci/saa7134/saa7134-empress.c | 1 + > > drivers/media/pci/saa7134/saa7134-reg.h | 7 + > > drivers/media/pci/saa7134/saa7134-tvaudio.c | 56 +++--- > > drivers/media/pci/saa7134/saa7134-video.c | 274 ++++++++++++++++++++++++++-- > > drivers/media/pci/saa7134/saa7134.h | 14 +- > > include/uapi/linux/videodev2.h | 2 + > > 6 files changed, 311 insertions(+), 43 deletions(-) > > > > diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c > > index 0a9047e..a150deb 100644 > > --- a/drivers/media/pci/saa7134/saa7134-empress.c > > +++ b/drivers/media/pci/saa7134/saa7134-empress.c > > @@ -262,6 +262,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { > > .vidioc_s_input = saa7134_s_input, > > .vidioc_s_std = saa7134_s_std, > > .vidioc_g_std = saa7134_g_std, > > + .vidioc_querystd = saa7134_querystd, > > .vidioc_log_status = v4l2_ctrl_log_status, > > .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, > > .vidioc_unsubscribe_event = v4l2_event_unsubscribe, > > diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h > > index e7e0af1..d3be05c 100644 > > --- a/drivers/media/pci/saa7134/saa7134-reg.h > > +++ b/drivers/media/pci/saa7134/saa7134-reg.h > > @@ -369,6 +369,13 @@ > > #define SAA7135_DSP_RWCLEAR_RERR 1 > > > > #define SAA7133_I2S_AUDIO_CONTROL 0x591 > > + > > +#define SAA7134_STDDETECT_AUFD (1 << 7) > > +#define SAA7134_STDDETECT_FCTC (1 << 2) > > +#define SAA7134_STDDETECT_LDEL (1 << 5) > > +#define SAA7134_STDDETECT_AUTO0 (1 << 1) > > +#define SAA7134_STDDETECT_AUTO1 (1 << 2) > > + > > /* ------------------------------------------------------------------ */ > > /* > > * Local variables: > > diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c > > index 0f34e09..6380e49 100644 > > --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c > > +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c > > @@ -315,7 +315,7 @@ static void tvaudio_setmode(struct saa7134_dev *dev, > > > > static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > > { > > - if (dev->thread.scan1 == dev->thread.scan2 && > > + if (dev->audio_thread.scan1 == dev->audio_thread.scan2 && > > !kthread_should_stop()) { > > if (timeout < 0) { > > set_current_state(TASK_INTERRUPTIBLE); > > @@ -325,7 +325,7 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) > > (msecs_to_jiffies(timeout)); > > } > > } > > - return dev->thread.scan1 != dev->thread.scan2; > > + return dev->audio_thread.scan1 != dev->audio_thread.scan2; > > } > > > > static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) > > @@ -488,8 +488,8 @@ static int tvaudio_thread(void *data) > > restart: > > try_to_freeze(); > > > > - dev->thread.scan1 = dev->thread.scan2; > > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > > dev->tvaudio = NULL; > > > > saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); > > @@ -528,7 +528,7 @@ static int tvaudio_thread(void *data) > > tvaudio_setmode(dev,&tvaudio[0],NULL); > > for (i = 0; i < ARRAY_SIZE(mainscan); i++) { > > carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); > > - if (dev->thread.scan1 != dev->thread.scan2) > > + if (dev->audio_thread.scan1 != dev->audio_thread.scan2) > > goto restart; > > } > > for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { > > @@ -604,11 +604,11 @@ static int tvaudio_thread(void *data) > > goto restart; > > if (kthread_should_stop()) > > break; > > - if (UNSET == dev->thread.mode) { > > + if (UNSET == dev->audio_thread.mode) { > > rx = tvaudio_getstereo(dev, &tvaudio[audio]); > > mode = saa7134_tvaudio_rx2mode(rx); > > } else { > > - mode = dev->thread.mode; > > + mode = dev->audio_thread.mode; > > } > > if (lastmode != mode) { > > tvaudio_setstereo(dev,&tvaudio[audio],mode); > > @@ -618,7 +618,7 @@ static int tvaudio_thread(void *data) > > } > > > > done: > > - dev->thread.stopped = 1; > > + dev->audio_thread.stopped = 1; > > return 0; > > } > > > > @@ -785,8 +785,8 @@ static int tvaudio_thread_ddep(void *data) > > restart: > > try_to_freeze(); > > > > - dev->thread.scan1 = dev->thread.scan2; > > - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); > > + dev->audio_thread.scan1 = dev->audio_thread.scan2; > > + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); > > > > if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { > > /* insmod option override */ > > @@ -803,16 +803,18 @@ static int tvaudio_thread_ddep(void *data) > > } > > } else { > > /* (let chip) scan for sound carrier */ > > + v4l2_std_id id = dev->tvnorm->id; > > norms = 0; > > - if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) > > + > > + if (id & (V4L2_STD_B | V4L2_STD_GH)) > > norms |= 0x04; > > - if (dev->tvnorm->id & V4L2_STD_PAL_I) > > + if (id & V4L2_STD_PAL_I) > > norms |= 0x20; > > - if (dev->tvnorm->id & V4L2_STD_DK) > > + if (id & V4L2_STD_DK) > > norms |= 0x08; > > - if (dev->tvnorm->id & V4L2_STD_MN) > > + if (id & V4L2_STD_MN) > > norms |= 0x40; > > - if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > > + if (id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) > > norms |= 0x10; > > if (0 == norms) > > norms = 0x7c; /* all */ > > @@ -862,7 +864,7 @@ static int tvaudio_thread_ddep(void *data) > > } > > > > done: > > - dev->thread.stopped = 1; > > + dev->audio_thread.stopped = 1; > > return 0; > > } > > > > @@ -1024,13 +1026,13 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) > > break; > > } > > > > - dev->thread.thread = NULL; > > - dev->thread.scan1 = dev->thread.scan2 = 0; > > + dev->audio_thread.thread = NULL; > > + dev->audio_thread.scan1 = dev->audio_thread.scan2 = 0; > > if (my_thread) { > > saa7134_tvaudio_init(dev); > > /* start tvaudio thread */ > > - dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > > - if (IS_ERR(dev->thread.thread)) { > > + dev->audio_thread.thread = kthread_run(my_thread, dev, "%s", dev->name); > > + if (IS_ERR(dev->audio_thread.thread)) { > > printk(KERN_WARNING "%s: kernel_thread() failed\n", > > dev->name); > > /* XXX: missing error handling here */ > > @@ -1051,8 +1053,8 @@ int saa7134_tvaudio_close(struct saa7134_dev *dev) > > int saa7134_tvaudio_fini(struct saa7134_dev *dev) > > { > > /* shutdown tvaudio thread */ > > - if (dev->thread.thread && !dev->thread.stopped) > > - kthread_stop(dev->thread.thread); > > + if (dev->audio_thread.thread && !dev->audio_thread.stopped) > > + kthread_stop(dev->audio_thread.thread); > > > > saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ > > return 0; > > @@ -1064,12 +1066,12 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) > > dprintk("sound IF not in use, skipping scan\n"); > > dev->automute = 0; > > saa7134_tvaudio_setmute(dev); > > - } else if (dev->thread.thread) { > > - dev->thread.mode = UNSET; > > - dev->thread.scan2++; > > + } else if (dev->audio_thread.thread) { > > + dev->audio_thread.mode = UNSET; > > + dev->audio_thread.scan2++; > > > > - if (!dev->insuspend && !dev->thread.stopped) > > - wake_up_process(dev->thread.thread); > > + if (!dev->insuspend && !dev->audio_thread.stopped) > > + wake_up_process(dev->audio_thread.thread); > > } else { > > dev->automute = 0; > > saa7134_tvaudio_setmute(dev); > > diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c > > index eb472b5..2528b6d 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> > > > > @@ -452,19 +455,28 @@ static void video_mux(struct saa7134_dev *dev, int input) > > > > static void saa7134_set_decoder(struct saa7134_dev *dev) > > { > > - int luma_control, sync_control, mux; > > + int luma_control, sync_control, chroma_ctrl1, analog_adc, vgate_misc, mux; > > > > struct saa7134_tvnorm *norm = dev->tvnorm; > > mux = card_in(dev, dev->ctl_input).vmux; > > > > luma_control = norm->luma_control; > > sync_control = norm->sync_control; > > + chroma_ctrl1 = norm->chroma_ctrl1; > > + analog_adc = 0x01; > > + vgate_misc = norm->vgate_misc; > > > > if (mux > 5) > > luma_control |= 0x80; /* svideo */ > > if (noninterlaced || dev->nosignal) > > sync_control |= 0x20; > > > > + /* switch on auto standard detection */ > > + sync_control |= SAA7134_STDDETECT_AUFD; > > + chroma_ctrl1 |= SAA7134_STDDETECT_AUTO0; > > + chroma_ctrl1 &= ~SAA7134_STDDETECT_FCTC; > > + luma_control &= ~SAA7134_STDDETECT_LDEL; > > + > > /* setup video decoder */ > > saa_writeb(SAA7134_INCR_DELAY, 0x08); > > saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); > > @@ -487,16 +499,16 @@ static void saa7134_set_decoder(struct saa7134_dev *dev) > > dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); > > > > saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); > > - saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); > > + saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1); > > saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); > > > > saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); > > saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); > > > > - saa_writeb(SAA7134_ANALOG_ADC, 0x01); > > + saa_writeb(SAA7134_ANALOG_ADC, analog_adc); > > saa_writeb(SAA7134_VGATE_START, 0x11); > > saa_writeb(SAA7134_VGATE_STOP, 0xfe); > > - saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); > > + saa_writeb(SAA7134_MISC_VGATE_MSB, vgate_misc); > > saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); > > saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); > > } > > @@ -1686,6 +1698,98 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) > > } > > EXPORT_SYMBOL_GPL(saa7134_g_std); > > > > +static v4l2_std_id saa7134_read_std(struct saa7134_dev* dev) > > +{ > > + static v4l2_std_id stds[] = { V4L2_STD_ALL, V4L2_STD_NTSC, V4L2_STD_PAL, V4L2_STD_SECAM }; > > + v4l2_std_id result = 0; > > + > > + u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1); > > + u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2); > > + > > + if (!(st2 & 0x1)) //RDCAP == 0 > > + result = V4L2_STD_ALL; > > + else > > + result = stds[st1 & 0x03]; > > + > > + return result; > > +} > > + > > +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)"; > > + } > > +} > > + > > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std) > > +{ > > + struct saa7134_dev *dev = video_drvdata(file); > > + > > + v4l2_std_id dcstd = saa7134_read_std(dev); > > + if (dcstd != V4L2_STD_ALL) > > + *std &= dcstd; > > + else > > + *std = dcstd; > > + > > + return 0; > > +} > > +EXPORT_SYMBOL_GPL(saa7134_querystd); > > + > > +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; > > + > > + set_freezable(); > > + for (;;) { > > + saa7134_std_sleep(dev,-1); > > + if (kthread_should_stop()) > > + goto done; > > +restart: > > + try_to_freeze(); > > + > > + atomic_set(&dev->std_thread.scan1, atomic_read(&dev->std_thread.scan2)); > > + > > + if (saa7134_std_sleep(dev,3000)) > > + goto restart; > > + dcstd = saa7134_read_std(dev); > > + > > + dprintk("Standard detected: %s", saa7134_std_to_str(dcstd)); > > + 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_cropcap(struct file *file, void *priv, > > struct v4l2_cropcap *cap) > > { > > @@ -1793,13 +1897,13 @@ int saa7134_s_tuner(struct file *file, void *priv, > > if (0 != t->index) > > return -EINVAL; > > > > - mode = dev->thread.mode; > > + mode = dev->audio_thread.mode; > > if (UNSET == mode) { > > rx = saa7134_tvaudio_getstereo(dev); > > mode = saa7134_tvaudio_rx2mode(rx); > > } > > if (mode != t->audmode) > > - dev->thread.mode = t->audmode; > > + dev->audio_thread.mode = t->audmode; > > > > return 0; > > } > > @@ -2053,6 +2157,18 @@ 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, > > @@ -2084,6 +2200,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { > > .vidioc_dqbuf = saa7134_dqbuf, > > .vidioc_s_std = saa7134_s_std, > > .vidioc_g_std = saa7134_g_std, > > + .vidioc_querystd = saa7134_querystd, > > .vidioc_enum_input = saa7134_enum_input, > > .vidioc_g_input = saa7134_g_input, > > .vidioc_s_input = saa7134_s_input, > > @@ -2103,7 +2220,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, > > }; > > > > @@ -2272,6 +2389,9 @@ 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); > > + > > /* free stuff */ > > saa7134_pgtable_free(dev->pci, &dev->pt_cap); > > saa7134_pgtable_free(dev->pci, &dev->pt_vbi); > > @@ -2324,23 +2444,144 @@ 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)) { > > + printk(KERN_ALERT "%s: kthread_run(saa7134_standard_detector_thread) failed\n", dev->name); > > + dev->std_thread.thread = NULL; > > + 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; > > } > > > > void saa7134_irq_video_signalchange(struct saa7134_dev *dev) > > { > > - static const char *st[] = { > > - "(no signal)", "NTSC", "PAL", "SECAM" }; > > - u32 st1,st2; > > + static const char* st1_6_hlck[] = > > + { > > + [0] = "HPLL is locked; clock LLC is locked to line frequency", > > + [1] = "HPLL is not locked" > > + }; > > + static const char* st1_5_sltca[] = > > + { > > + [0] = "Video AGC is in normal operation mode", > > + [1] = "Video AGC is in gain recover mode (increase) after peak attack" > > + }; > > + static const char* st1_4_glimt[] = > > + { > > + [0] = "Video AGC is in normal operation mode", > > + [1] = "Video AGC is on its top limit, i.e. input signal is too large, out of AGC range" > > + }; > > + static const char* st1_3_glimb[] = > > + { > > + [0] = "video AGC is in normal operation mode", > > + [1] = "video AGC is on its bottom limit. Input signal is too small, out of AGC range" > > + }; > > + static const char* st1_2_wipa[] = > > + { > > + [0] = "White or Color Peak Control loop is not activated", > > + [1] = "White or Color Peak Control was triggered in previous line" > > + }; > > + struct st1_1_0_dcstd_descr > > + { > > + const char* name; > > + v4l2_std_id stdid; > > + }; > > + static struct st1_1_0_dcstd_descr st1_1_0_dcstd[] = > > + { > > + [0] = { "No color signal could be detected. The video is assumed being B/W", V4L2_STD_UNKNOWN}, > > + [1] = { "NTSC signal detected", V4L2_STD_NTSC}, > > + [2] = { "PAL signal detected", V4L2_STD_PAL}, > > + [3] = { "SECAM signal detected", V4L2_STD_SECAM} > > + }; > > + static const char* st2_7_intl[] = > > + { > > + [0] = "Video input is detected as non-interlaced", > > + [1] = "Video input is detected as interlaced" > > + }; > > + static const char* st2_6_hlvln[] = > > + { > > + [0] = "Horizontal and vertical synchronization is achieved", > > + [1] = "Either horizontal or vertical synchronization is lost" > > + }; > > + static const char* st2_5_fidt[] = > > + { > > + [0] = "Video input is detected with 50 Hz field rate", > > + [1] = "Video input is detected with 60 Hz field rate" > > + }; > > + static const char* st2_3_type3[] = > > + { > > + [0] = "normal video signal", > > + [1] = "Macrovision copy protection type 3 detected" > > + }; > > + static const char* st2_2_colstr[] = > > + { > > + [0] = "normal video signal", > > + [1] = "Macrovision Color Stripe scheme detected" > > + }; > > + static const char* st2_1_copro[] = > > + { > > + [0] = "normal video signal, No Macrovision copy protection scheme detected", > > + [1] = "Copy protection detected according Macrovision (including 7.01)" > > + }; > > + static const char* st2_0_rdcap[] = > > + { > > + [0] = "One or more of the decoder control loops are not locked", > > + [1] = "Ready for capture. All control loops are locked: Horizontal, Vertical, color subcarrier, chroma gain control" > > + }; > > + > > + u8 st1,st2; > > > > st1 = saa_readb(SAA7134_STATUS_VIDEO1); > > st2 = saa_readb(SAA7134_STATUS_VIDEO2); > > - dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", > > - (st1 & 0x40) ? "not locked" : "locked", > > - (st2 & 0x40) ? "no" : "yes", > > - st[st1 & 0x03]); > > - dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > > > > + dprintk("saa7134: DEBUG. Status byte 1:\n" > > + "HLCK = %s\n" > > + "SLTCA = %s\n" > > + "GLIMT = %s\n" > > + "GLIMB = %s\n" > > + "WIPA = %s\n" > > + "DCSTD = %s\n", > > + st1_6_hlck[((st1 & (1 << 6)) ? 1 : 0)], > > + st1_5_sltca[((st1 & (1 << 5)) ? 1 : 0)], > > + st1_4_glimt[((st1 & (1 << 4)) ? 1 : 0)], > > + st1_3_glimb[((st1 & (1 << 3)) ? 1 : 0)], > > + st1_2_wipa[((st1 & (1 << 2)) ? 1 : 0)], > > + st1_1_0_dcstd[st1 & 0x03].name > > + ); > > + dprintk("saa7134: DEBUG. Status byte 2:\n" > > + "INTL = %s\n" > > + "HLVLN = %s\n" > > + "FIDT = %s\n" > > + "TYPE3 = %s\n" > > + "COLSTR = %s\n" > > + "COPRO = %s\n" > > + "RDCAP = %s\n", > > + st2_7_intl[((st2 & (1 << 7)) ? 1 : 0)], > > + st2_6_hlvln[((st2 & (1 << 6)) ? 1 : 0)], > > + st2_5_fidt[((st2 & (1 << 5)) ? 1 : 0)], > > + st2_3_type3[((st2 & (1 << 3)) ? 1 : 0)], > > + st2_2_colstr[((st2 & (1 << 2)) ? 1 : 0)], > > + st2_1_copro[((st2 & (1 << 1)) ? 1 : 0)], > > + st2_0_rdcap[((st2 & (1 << 0)) ? 1 : 0)] > > + ); > > + > > + dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); > > if (dev->nosignal) { > > /* no video signal -> mute audio */ > > if (dev->ctl_automute) > > @@ -2358,6 +2599,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); > > } > > > > > > @@ -2393,6 +2636,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) > > spin_unlock(&dev->slock); > > } > > > > + > > /* ----------------------------------------------------------- */ > > /* > > * Local variables: > > diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h > > index 2474e84..0e5bd3d 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 */ > > @@ -623,7 +633,8 @@ struct saa7134_dev { > > > > /* other global state info */ > > unsigned int automute; > > - struct saa7134_thread thread; > > + struct saa7134_thread audio_thread; > > + struct saa7134_thread_std std_thread; > > struct saa7134_input *input; > > struct saa7134_input *hw_input; > > unsigned int hw_mute; > > @@ -779,6 +790,7 @@ extern struct video_device saa7134_radio_template; > > > > int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id); > > int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); > > +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std); > > int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); > > int saa7134_g_input(struct file *file, void *priv, unsigned int *i); > > int saa7134_s_input(struct file *file, void *priv, unsigned int i); > > diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h > > index e35ad6c..806057b 100644 > > --- a/include/uapi/linux/videodev2.h > > +++ b/include/uapi/linux/videodev2.h > > @@ -1765,8 +1765,10 @@ struct v4l2_streamparm { > > #define V4L2_EVENT_EOS 2 > > #define V4L2_EVENT_CTRL 3 > > #define V4L2_EVENT_FRAME_SYNC 4 > > +#define V4L2_EVENT_SIGNALCHANGED 5 > > #define V4L2_EVENT_PRIVATE_START 0x08000000 > > > > + > > /* Payload for V4L2_EVENT_VSYNC */ > > struct v4l2_event_vsync { > > /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */ > > -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 03/28/2014 10:51 AM, Mikhail Domrachev wrote: > Hi Hans, > > Thank you for comments, I will rework the patch and document the new > event type. > > Let me explain why I created a new thread. > My company is engaged in the monitoring of TV air. All TV channels are > recorded 24/7 for further analysis. But some local TV channels change > the standard over time (SECAM->PAL, PAL->SECAM). So the recording > software must be notified about these changes to set a new standard and > record the picture but not the noise. OK, fair enough. Once I receive the reworked version I'll review again. Expect that you probably will need to make at least one other revision after that as I did not do an in-depth review yet. It might also be a good idea to look at this: http://git.linuxtv.org/hverkuil/media_tree.git/shortlog/refs/heads/saa7134 The patches in that tree convert the saa7134 driver to the videobuf2 framework. This is a huge change that should make the driver a lot more stable and more compliant to the v4l2 specification. If your company is using this driver, then I would recommend that you test this. A pull request was made for this for kernel 3.16 and I tested it quite extensively, but if you can test it as well then that would be very useful. Regards, Hans > > Regards, > Mikhail > > On Fri, 2014-03-28 at 09:37 +0100, Hans Verkuil wrote: >> Hi Mikhail, >> >> Thank you for the patch. However, it does need some work before I can accept it. >> >> First of all, run your patch through scripts/checkpatch.pl to ensure it complies >> to the kernel coding style. >> >> Secondly, split up this single patch in smaller ones: in particular the addition >> of the new event type needs to be in a patch of its own. >> >> Thirdly, you need to document the new event type in the DocBook documentation as >> well. API additions are only accepted if the documentation is updated at the same >> time. >> >> I also wonder why you need a thread to watch for signal changes. It's not wrong, >> but in practice a TV input signal rarely if ever changes format. It can be different >> between different countries or when testing with a signal generator, but the normal >> case is that you are just interested in the current standard, and not how it might >> change over time. That would simplify the code a lot. This is what other drivers >> that implement querystd do. >> >> Regards, >> >> Hans -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
>> Let me explain why I created a new thread. >> My company is engaged in the monitoring of TV air. All TV channels are >> recorded 24/7 for further analysis. But some local TV channels change >> the standard over time (SECAM->PAL, PAL->SECAM). So the recording >> software must be notified about these changes to set a new standard and >> record the picture but not the noise. > > OK, fair enough. This is a perfectly reasonable use case, but since we don't do this with any other devices we probably need to decide whether this really should be the responsibility of the kernel at all, or whether it really should be done in userland. Doing it in userland would be trivial (even just a script which periodically runs QUERYSTD in a loop would accomplish the same thing), and the extra complexity of having a thread combined with the inconsistent behavior with all the other drivers might make it more worthwhile to do it in userland. If it were hooked to an interrupt line on the video decoder, I could certainly see doing it in kernel, but for something like this the loop that checks the standard could just as easily be done in userland. Devin
Hi Mikhail, On 03/28/2014 02:16 PM, Devin Heitmueller wrote: >>> Let me explain why I created a new thread. >>> My company is engaged in the monitoring of TV air. All TV channels are >>> recorded 24/7 for further analysis. But some local TV channels change >>> the standard over time (SECAM->PAL, PAL->SECAM). So the recording >>> software must be notified about these changes to set a new standard and >>> record the picture but not the noise. >> >> OK, fair enough. > > This is a perfectly reasonable use case, but since we don't do this > with any other devices we probably need to decide whether this really > should be the responsibility of the kernel at all, or whether it > really should be done in userland. Doing it in userland would be > trivial (even just a script which periodically runs QUERYSTD in a loop > would accomplish the same thing), and the extra complexity of having a > thread combined with the inconsistent behavior with all the other > drivers might make it more worthwhile to do it in userland. > > If it were hooked to an interrupt line on the video decoder, I could > certainly see doing it in kernel, but for something like this the loop > that checks the standard could just as easily be done in userland. I agree with Devin here. None of the existing SDTV receivers do this, and nobody ever used interrupts to check for this. Such interrupts are rarely available, and if they exists they are never hooked up. This is quite different for HDTV receivers where such an event is pretty much required (even though it still isn't officially added to the kernel, but that's another story). Is there any reason why your application cannot periodically call QUERYSTD? The problem you have is not specific to the saa7134, so moving it to the application is actually a more generic solution since it will work with any driver that implements querystd. Adding querystd support to saa7134 in the first place is a good idea regardless, and I'll test your patch today or Friday. Regards, Hans -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi, Hans, > I agree with Devin here. None of the existing SDTV receivers do this, and > nobody ever used interrupts to check for this. Such interrupts are rarely > available, and if they exists they are never hooked up. This is quite > different for HDTV receivers where such an event is pretty much required > (even though it still isn't officially added to the kernel, but that's > another story). OK, I got it. > Is there any reason why your application cannot periodically call QUERYSTD? There's no reason, it can. > I'll test your patch today or Friday. OK. Thank you.
diff --git a/drivers/media/pci/saa7134/saa7134-empress.c b/drivers/media/pci/saa7134/saa7134-empress.c index 0a9047e..a150deb 100644 --- a/drivers/media/pci/saa7134/saa7134-empress.c +++ b/drivers/media/pci/saa7134/saa7134-empress.c @@ -262,6 +262,7 @@ static const struct v4l2_ioctl_ops ts_ioctl_ops = { .vidioc_s_input = saa7134_s_input, .vidioc_s_std = saa7134_s_std, .vidioc_g_std = saa7134_g_std, + .vidioc_querystd = saa7134_querystd, .vidioc_log_status = v4l2_ctrl_log_status, .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, diff --git a/drivers/media/pci/saa7134/saa7134-reg.h b/drivers/media/pci/saa7134/saa7134-reg.h index e7e0af1..d3be05c 100644 --- a/drivers/media/pci/saa7134/saa7134-reg.h +++ b/drivers/media/pci/saa7134/saa7134-reg.h @@ -369,6 +369,13 @@ #define SAA7135_DSP_RWCLEAR_RERR 1 #define SAA7133_I2S_AUDIO_CONTROL 0x591 + +#define SAA7134_STDDETECT_AUFD (1 << 7) +#define SAA7134_STDDETECT_FCTC (1 << 2) +#define SAA7134_STDDETECT_LDEL (1 << 5) +#define SAA7134_STDDETECT_AUTO0 (1 << 1) +#define SAA7134_STDDETECT_AUTO1 (1 << 2) + /* ------------------------------------------------------------------ */ /* * Local variables: diff --git a/drivers/media/pci/saa7134/saa7134-tvaudio.c b/drivers/media/pci/saa7134/saa7134-tvaudio.c index 0f34e09..6380e49 100644 --- a/drivers/media/pci/saa7134/saa7134-tvaudio.c +++ b/drivers/media/pci/saa7134/saa7134-tvaudio.c @@ -315,7 +315,7 @@ static void tvaudio_setmode(struct saa7134_dev *dev, static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) { - if (dev->thread.scan1 == dev->thread.scan2 && + if (dev->audio_thread.scan1 == dev->audio_thread.scan2 && !kthread_should_stop()) { if (timeout < 0) { set_current_state(TASK_INTERRUPTIBLE); @@ -325,7 +325,7 @@ static int tvaudio_sleep(struct saa7134_dev *dev, int timeout) (msecs_to_jiffies(timeout)); } } - return dev->thread.scan1 != dev->thread.scan2; + return dev->audio_thread.scan1 != dev->audio_thread.scan2; } static int tvaudio_checkcarrier(struct saa7134_dev *dev, struct mainscan *scan) @@ -488,8 +488,8 @@ static int tvaudio_thread(void *data) restart: try_to_freeze(); - dev->thread.scan1 = dev->thread.scan2; - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + dev->audio_thread.scan1 = dev->audio_thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); dev->tvaudio = NULL; saa_writeb(SAA7134_MONITOR_SELECT, 0xa0); @@ -528,7 +528,7 @@ static int tvaudio_thread(void *data) tvaudio_setmode(dev,&tvaudio[0],NULL); for (i = 0; i < ARRAY_SIZE(mainscan); i++) { carr_vals[i] = tvaudio_checkcarrier(dev, mainscan+i); - if (dev->thread.scan1 != dev->thread.scan2) + if (dev->audio_thread.scan1 != dev->audio_thread.scan2) goto restart; } for (max1 = 0, max2 = 0, i = 0; i < ARRAY_SIZE(mainscan); i++) { @@ -604,11 +604,11 @@ static int tvaudio_thread(void *data) goto restart; if (kthread_should_stop()) break; - if (UNSET == dev->thread.mode) { + if (UNSET == dev->audio_thread.mode) { rx = tvaudio_getstereo(dev, &tvaudio[audio]); mode = saa7134_tvaudio_rx2mode(rx); } else { - mode = dev->thread.mode; + mode = dev->audio_thread.mode; } if (lastmode != mode) { tvaudio_setstereo(dev,&tvaudio[audio],mode); @@ -618,7 +618,7 @@ static int tvaudio_thread(void *data) } done: - dev->thread.stopped = 1; + dev->audio_thread.stopped = 1; return 0; } @@ -785,8 +785,8 @@ static int tvaudio_thread_ddep(void *data) restart: try_to_freeze(); - dev->thread.scan1 = dev->thread.scan2; - dprintk("tvaudio thread scan start [%d]\n",dev->thread.scan1); + dev->audio_thread.scan1 = dev->audio_thread.scan2; + dprintk("tvaudio thread scan start [%d]\n",dev->audio_thread.scan1); if (audio_ddep >= 0x04 && audio_ddep <= 0x0e) { /* insmod option override */ @@ -803,16 +803,18 @@ static int tvaudio_thread_ddep(void *data) } } else { /* (let chip) scan for sound carrier */ + v4l2_std_id id = dev->tvnorm->id; norms = 0; - if (dev->tvnorm->id & (V4L2_STD_B | V4L2_STD_GH)) + + if (id & (V4L2_STD_B | V4L2_STD_GH)) norms |= 0x04; - if (dev->tvnorm->id & V4L2_STD_PAL_I) + if (id & V4L2_STD_PAL_I) norms |= 0x20; - if (dev->tvnorm->id & V4L2_STD_DK) + if (id & V4L2_STD_DK) norms |= 0x08; - if (dev->tvnorm->id & V4L2_STD_MN) + if (id & V4L2_STD_MN) norms |= 0x40; - if (dev->tvnorm->id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) + if (id & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) norms |= 0x10; if (0 == norms) norms = 0x7c; /* all */ @@ -862,7 +864,7 @@ static int tvaudio_thread_ddep(void *data) } done: - dev->thread.stopped = 1; + dev->audio_thread.stopped = 1; return 0; } @@ -1024,13 +1026,13 @@ int saa7134_tvaudio_init2(struct saa7134_dev *dev) break; } - dev->thread.thread = NULL; - dev->thread.scan1 = dev->thread.scan2 = 0; + dev->audio_thread.thread = NULL; + dev->audio_thread.scan1 = dev->audio_thread.scan2 = 0; if (my_thread) { saa7134_tvaudio_init(dev); /* start tvaudio thread */ - dev->thread.thread = kthread_run(my_thread, dev, "%s", dev->name); - if (IS_ERR(dev->thread.thread)) { + dev->audio_thread.thread = kthread_run(my_thread, dev, "%s", dev->name); + if (IS_ERR(dev->audio_thread.thread)) { printk(KERN_WARNING "%s: kernel_thread() failed\n", dev->name); /* XXX: missing error handling here */ @@ -1051,8 +1053,8 @@ int saa7134_tvaudio_close(struct saa7134_dev *dev) int saa7134_tvaudio_fini(struct saa7134_dev *dev) { /* shutdown tvaudio thread */ - if (dev->thread.thread && !dev->thread.stopped) - kthread_stop(dev->thread.thread); + if (dev->audio_thread.thread && !dev->audio_thread.stopped) + kthread_stop(dev->audio_thread.thread); saa_andorb(SAA7134_ANALOG_IO_SELECT, 0x07, 0x00); /* LINE1 */ return 0; @@ -1064,12 +1066,12 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev) dprintk("sound IF not in use, skipping scan\n"); dev->automute = 0; saa7134_tvaudio_setmute(dev); - } else if (dev->thread.thread) { - dev->thread.mode = UNSET; - dev->thread.scan2++; + } else if (dev->audio_thread.thread) { + dev->audio_thread.mode = UNSET; + dev->audio_thread.scan2++; - if (!dev->insuspend && !dev->thread.stopped) - wake_up_process(dev->thread.thread); + if (!dev->insuspend && !dev->audio_thread.stopped) + wake_up_process(dev->audio_thread.thread); } else { dev->automute = 0; saa7134_tvaudio_setmute(dev); diff --git a/drivers/media/pci/saa7134/saa7134-video.c b/drivers/media/pci/saa7134/saa7134-video.c index eb472b5..2528b6d 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> @@ -452,19 +455,28 @@ static void video_mux(struct saa7134_dev *dev, int input) static void saa7134_set_decoder(struct saa7134_dev *dev) { - int luma_control, sync_control, mux; + int luma_control, sync_control, chroma_ctrl1, analog_adc, vgate_misc, mux; struct saa7134_tvnorm *norm = dev->tvnorm; mux = card_in(dev, dev->ctl_input).vmux; luma_control = norm->luma_control; sync_control = norm->sync_control; + chroma_ctrl1 = norm->chroma_ctrl1; + analog_adc = 0x01; + vgate_misc = norm->vgate_misc; if (mux > 5) luma_control |= 0x80; /* svideo */ if (noninterlaced || dev->nosignal) sync_control |= 0x20; + /* switch on auto standard detection */ + sync_control |= SAA7134_STDDETECT_AUFD; + chroma_ctrl1 |= SAA7134_STDDETECT_AUTO0; + chroma_ctrl1 &= ~SAA7134_STDDETECT_FCTC; + luma_control &= ~SAA7134_STDDETECT_LDEL; + /* setup video decoder */ saa_writeb(SAA7134_INCR_DELAY, 0x08); saa_writeb(SAA7134_ANALOG_IN_CTRL1, 0xc0 | mux); @@ -487,16 +499,16 @@ static void saa7134_set_decoder(struct saa7134_dev *dev) dev->ctl_invert ? -dev->ctl_saturation : dev->ctl_saturation); saa_writeb(SAA7134_DEC_CHROMA_HUE, dev->ctl_hue); - saa_writeb(SAA7134_CHROMA_CTRL1, norm->chroma_ctrl1); + saa_writeb(SAA7134_CHROMA_CTRL1, chroma_ctrl1); saa_writeb(SAA7134_CHROMA_GAIN, norm->chroma_gain); saa_writeb(SAA7134_CHROMA_CTRL2, norm->chroma_ctrl2); saa_writeb(SAA7134_MODE_DELAY_CTRL, 0x00); - saa_writeb(SAA7134_ANALOG_ADC, 0x01); + saa_writeb(SAA7134_ANALOG_ADC, analog_adc); saa_writeb(SAA7134_VGATE_START, 0x11); saa_writeb(SAA7134_VGATE_STOP, 0xfe); - saa_writeb(SAA7134_MISC_VGATE_MSB, norm->vgate_misc); + saa_writeb(SAA7134_MISC_VGATE_MSB, vgate_misc); saa_writeb(SAA7134_RAW_DATA_GAIN, 0x40); saa_writeb(SAA7134_RAW_DATA_OFFSET, 0x80); } @@ -1686,6 +1698,98 @@ int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id) } EXPORT_SYMBOL_GPL(saa7134_g_std); +static v4l2_std_id saa7134_read_std(struct saa7134_dev* dev) +{ + static v4l2_std_id stds[] = { V4L2_STD_ALL, V4L2_STD_NTSC, V4L2_STD_PAL, V4L2_STD_SECAM }; + v4l2_std_id result = 0; + + u8 st1 = saa_readb(SAA7134_STATUS_VIDEO1); + u8 st2 = saa_readb(SAA7134_STATUS_VIDEO2); + + if (!(st2 & 0x1)) //RDCAP == 0 + result = V4L2_STD_ALL; + else + result = stds[st1 & 0x03]; + + return result; +} + +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)"; + } +} + +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct saa7134_dev *dev = video_drvdata(file); + + v4l2_std_id dcstd = saa7134_read_std(dev); + if (dcstd != V4L2_STD_ALL) + *std &= dcstd; + else + *std = dcstd; + + return 0; +} +EXPORT_SYMBOL_GPL(saa7134_querystd); + +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; + + set_freezable(); + for (;;) { + saa7134_std_sleep(dev,-1); + if (kthread_should_stop()) + goto done; +restart: + try_to_freeze(); + + atomic_set(&dev->std_thread.scan1, atomic_read(&dev->std_thread.scan2)); + + if (saa7134_std_sleep(dev,3000)) + goto restart; + dcstd = saa7134_read_std(dev); + + dprintk("Standard detected: %s", saa7134_std_to_str(dcstd)); + 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_cropcap(struct file *file, void *priv, struct v4l2_cropcap *cap) { @@ -1793,13 +1897,13 @@ int saa7134_s_tuner(struct file *file, void *priv, if (0 != t->index) return -EINVAL; - mode = dev->thread.mode; + mode = dev->audio_thread.mode; if (UNSET == mode) { rx = saa7134_tvaudio_getstereo(dev); mode = saa7134_tvaudio_rx2mode(rx); } if (mode != t->audmode) - dev->thread.mode = t->audmode; + dev->audio_thread.mode = t->audmode; return 0; } @@ -2053,6 +2157,18 @@ 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, @@ -2084,6 +2200,7 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = { .vidioc_dqbuf = saa7134_dqbuf, .vidioc_s_std = saa7134_s_std, .vidioc_g_std = saa7134_g_std, + .vidioc_querystd = saa7134_querystd, .vidioc_enum_input = saa7134_enum_input, .vidioc_g_input = saa7134_g_input, .vidioc_s_input = saa7134_s_input, @@ -2103,7 +2220,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, }; @@ -2272,6 +2389,9 @@ 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); + /* free stuff */ saa7134_pgtable_free(dev->pci, &dev->pt_cap); saa7134_pgtable_free(dev->pci, &dev->pt_vbi); @@ -2324,23 +2444,144 @@ 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)) { + printk(KERN_ALERT "%s: kthread_run(saa7134_standard_detector_thread) failed\n", dev->name); + dev->std_thread.thread = NULL; + 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; } void saa7134_irq_video_signalchange(struct saa7134_dev *dev) { - static const char *st[] = { - "(no signal)", "NTSC", "PAL", "SECAM" }; - u32 st1,st2; + static const char* st1_6_hlck[] = + { + [0] = "HPLL is locked; clock LLC is locked to line frequency", + [1] = "HPLL is not locked" + }; + static const char* st1_5_sltca[] = + { + [0] = "Video AGC is in normal operation mode", + [1] = "Video AGC is in gain recover mode (increase) after peak attack" + }; + static const char* st1_4_glimt[] = + { + [0] = "Video AGC is in normal operation mode", + [1] = "Video AGC is on its top limit, i.e. input signal is too large, out of AGC range" + }; + static const char* st1_3_glimb[] = + { + [0] = "video AGC is in normal operation mode", + [1] = "video AGC is on its bottom limit. Input signal is too small, out of AGC range" + }; + static const char* st1_2_wipa[] = + { + [0] = "White or Color Peak Control loop is not activated", + [1] = "White or Color Peak Control was triggered in previous line" + }; + struct st1_1_0_dcstd_descr + { + const char* name; + v4l2_std_id stdid; + }; + static struct st1_1_0_dcstd_descr st1_1_0_dcstd[] = + { + [0] = { "No color signal could be detected. The video is assumed being B/W", V4L2_STD_UNKNOWN}, + [1] = { "NTSC signal detected", V4L2_STD_NTSC}, + [2] = { "PAL signal detected", V4L2_STD_PAL}, + [3] = { "SECAM signal detected", V4L2_STD_SECAM} + }; + static const char* st2_7_intl[] = + { + [0] = "Video input is detected as non-interlaced", + [1] = "Video input is detected as interlaced" + }; + static const char* st2_6_hlvln[] = + { + [0] = "Horizontal and vertical synchronization is achieved", + [1] = "Either horizontal or vertical synchronization is lost" + }; + static const char* st2_5_fidt[] = + { + [0] = "Video input is detected with 50 Hz field rate", + [1] = "Video input is detected with 60 Hz field rate" + }; + static const char* st2_3_type3[] = + { + [0] = "normal video signal", + [1] = "Macrovision copy protection type 3 detected" + }; + static const char* st2_2_colstr[] = + { + [0] = "normal video signal", + [1] = "Macrovision Color Stripe scheme detected" + }; + static const char* st2_1_copro[] = + { + [0] = "normal video signal, No Macrovision copy protection scheme detected", + [1] = "Copy protection detected according Macrovision (including 7.01)" + }; + static const char* st2_0_rdcap[] = + { + [0] = "One or more of the decoder control loops are not locked", + [1] = "Ready for capture. All control loops are locked: Horizontal, Vertical, color subcarrier, chroma gain control" + }; + + u8 st1,st2; st1 = saa_readb(SAA7134_STATUS_VIDEO1); st2 = saa_readb(SAA7134_STATUS_VIDEO2); - dprintk("DCSDT: pll: %s, sync: %s, norm: %s\n", - (st1 & 0x40) ? "not locked" : "locked", - (st2 & 0x40) ? "no" : "yes", - st[st1 & 0x03]); - dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); + dprintk("saa7134: DEBUG. Status byte 1:\n" + "HLCK = %s\n" + "SLTCA = %s\n" + "GLIMT = %s\n" + "GLIMB = %s\n" + "WIPA = %s\n" + "DCSTD = %s\n", + st1_6_hlck[((st1 & (1 << 6)) ? 1 : 0)], + st1_5_sltca[((st1 & (1 << 5)) ? 1 : 0)], + st1_4_glimt[((st1 & (1 << 4)) ? 1 : 0)], + st1_3_glimb[((st1 & (1 << 3)) ? 1 : 0)], + st1_2_wipa[((st1 & (1 << 2)) ? 1 : 0)], + st1_1_0_dcstd[st1 & 0x03].name + ); + dprintk("saa7134: DEBUG. Status byte 2:\n" + "INTL = %s\n" + "HLVLN = %s\n" + "FIDT = %s\n" + "TYPE3 = %s\n" + "COLSTR = %s\n" + "COPRO = %s\n" + "RDCAP = %s\n", + st2_7_intl[((st2 & (1 << 7)) ? 1 : 0)], + st2_6_hlvln[((st2 & (1 << 6)) ? 1 : 0)], + st2_5_fidt[((st2 & (1 << 5)) ? 1 : 0)], + st2_3_type3[((st2 & (1 << 3)) ? 1 : 0)], + st2_2_colstr[((st2 & (1 << 2)) ? 1 : 0)], + st2_1_copro[((st2 & (1 << 1)) ? 1 : 0)], + st2_0_rdcap[((st2 & (1 << 0)) ? 1 : 0)] + ); + + dev->nosignal = (st1 & 0x40) || (st2 & 0x40) || !(st2 & 0x1); if (dev->nosignal) { /* no video signal -> mute audio */ if (dev->ctl_automute) @@ -2358,6 +2599,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); } @@ -2393,6 +2636,7 @@ void saa7134_irq_video_done(struct saa7134_dev *dev, unsigned long status) spin_unlock(&dev->slock); } + /* ----------------------------------------------------------- */ /* * Local variables: diff --git a/drivers/media/pci/saa7134/saa7134.h b/drivers/media/pci/saa7134/saa7134.h index 2474e84..0e5bd3d 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 */ @@ -623,7 +633,8 @@ struct saa7134_dev { /* other global state info */ unsigned int automute; - struct saa7134_thread thread; + struct saa7134_thread audio_thread; + struct saa7134_thread_std std_thread; struct saa7134_input *input; struct saa7134_input *hw_input; unsigned int hw_mute; @@ -779,6 +790,7 @@ extern struct video_device saa7134_radio_template; int saa7134_s_std(struct file *file, void *priv, v4l2_std_id id); int saa7134_g_std(struct file *file, void *priv, v4l2_std_id *id); +int saa7134_querystd(struct file *file, void *priv, v4l2_std_id *std); int saa7134_enum_input(struct file *file, void *priv, struct v4l2_input *i); int saa7134_g_input(struct file *file, void *priv, unsigned int *i); int saa7134_s_input(struct file *file, void *priv, unsigned int i); diff --git a/include/uapi/linux/videodev2.h b/include/uapi/linux/videodev2.h index e35ad6c..806057b 100644 --- a/include/uapi/linux/videodev2.h +++ b/include/uapi/linux/videodev2.h @@ -1765,8 +1765,10 @@ struct v4l2_streamparm { #define V4L2_EVENT_EOS 2 #define V4L2_EVENT_CTRL 3 #define V4L2_EVENT_FRAME_SYNC 4 +#define V4L2_EVENT_SIGNALCHANGED 5 #define V4L2_EVENT_PRIVATE_START 0x08000000 + /* Payload for V4L2_EVENT_VSYNC */ struct v4l2_event_vsync { /* Can be V4L2_FIELD_ANY, _NONE, _TOP or _BOTTOM */
saa7134: automatic norm detection switched on saa7134: vidioc_querystd added saa7134: notification about TV norm changes via V4L2 event interface added videodev2: new event type added Signed-off-by: Mikhail Domrachev <mihail.domrychev@comexp.ru> --- drivers/media/pci/saa7134/saa7134-empress.c | 1 + drivers/media/pci/saa7134/saa7134-reg.h | 7 + drivers/media/pci/saa7134/saa7134-tvaudio.c | 56 +++--- drivers/media/pci/saa7134/saa7134-video.c | 274 ++++++++++++++++++++++++++-- drivers/media/pci/saa7134/saa7134.h | 14 +- include/uapi/linux/videodev2.h | 2 + 6 files changed, 311 insertions(+), 43 deletions(-)