On Fri Sep 18 18:07:42 2026 -0400, David Wood wrote:
> Non-interlaced 60 Hz sources, such as the 240p output of classic game
> consoles, produce 262 lines per field with no half-line offset, so every
> field has the same parity. The TVP5150 tracks such a signal without
> trouble (locked, 60 Hz, vertical line count 524 instead of 525) but
> reports the same field ID for every field, and the em2860 forwards that
> in its field-start header. On an EM2860/TVP5150 reference design
> (eb1a:5051) fed by a NES, 112 of 113 consecutive headers in a 6 s stream
> carry field ID 1 (bottom); the lone top-field header is the first one.
> 
> finish_field_prepare_next() only completes a buffer and starts the next
> one when a top field arrives, so with such a source no frame is ever
> delivered: streaming starts, the first buffer never finishes, and the
> application sits in select() until it times out. Disturbing the cable
> briefly loses sync, yields a stray top-field header and lets a single
> frame through -- the "one frame, then it freezes" symptom.
> 
> Track the previous field ID. When a header repeats the ID of the field
> before it, give the new field the opposite parity instead of taking the
> ID literally, so consecutive fields are woven into a frame the same way
> a genuine top/bottom pair is. Interlaced streams alternate IDs and are
> unaffected. A dropped field in one now lands two bottom fields in the
> same buffer, which the copy path already tolerates, and the following
> top field starts a new frame as before.
> 
> With this the NES streams at 29.9 fps with a stable, correctly woven
> picture. Each frame holds two consecutive progressive source frames, so
> an application that wants the source's 60 fps back can bob-deinterlace.
> 
> Signed-off-by: David Wood <[email protected]>
> Signed-off-by: Hans Verkuil <[email protected]>

Patch committed.

Thanks,
Hans Verkuil

 drivers/media/usb/em28xx/em28xx-video.c | 26 ++++++++++++++++++++++++--
 drivers/media/usb/em28xx/em28xx.h       |  1 +
 2 files changed, 25 insertions(+), 2 deletions(-)

---

diff --git a/drivers/media/usb/em28xx/em28xx-video.c 
b/drivers/media/usb/em28xx/em28xx-video.c
index 186a3043c609..a778bc4f61fb 100644
--- a/drivers/media/usb/em28xx/em28xx-video.c
+++ b/drivers/media/usb/em28xx/em28xx-video.c
@@ -744,6 +744,27 @@ finish_field_prepare_next(struct em28xx *dev,
        return buf;
 }
 
+/*
+ * Set the parity of the field that starts with this header.
+ *
+ * Non-interlaced sources, such as the 240p output of classic game consoles,
+ * generate every field with the same parity, so the bridge reports the same
+ * field ID over and over. A top field never arrives and no frame is ever
+ * completed. Detect a repeated field ID and alternate the parity instead, so
+ * consecutive fields are woven into a frame like a genuine interlaced pair.
+ */
+static inline void em28xx_set_field_parity(struct em28xx_v4l2 *v4l2,
+                                          int field_id)
+{
+       bool top_field = !(field_id & 1);
+
+       if (field_id == v4l2->last_field_id)
+               top_field = !v4l2->top_field;
+
+       v4l2->last_field_id = field_id;
+       v4l2->top_field = top_field;
+}
+
 /*
  * Process data packet according to the em2710/em2750/em28xx frame data format
  */
@@ -778,14 +799,14 @@ static inline void process_frame_data_em28xx(struct 
em28xx *dev,
                        v4l2->capture_type = 0;
                        v4l2->vbi_read = 0;
                        em28xx_isocdbg("VBI START HEADER !!!\n");
-                       v4l2->top_field = !(data_pkt[2] & 1);
+                       em28xx_set_field_parity(v4l2, data_pkt[2] & 1);
                        data_pkt += 4;
                        data_len -= 4;
                } else if (data_pkt[0] == 0x22 && data_pkt[1] == 0x5a) {
                        /* Field start (VBI disabled) */
                        v4l2->capture_type = 2;
                        em28xx_isocdbg("VIDEO START HEADER !!!\n");
-                       v4l2->top_field = !(data_pkt[2] & 1);
+                       em28xx_set_field_parity(v4l2, data_pkt[2] & 1);
                        data_pkt += 4;
                        data_len -= 4;
                }
@@ -1246,6 +1267,7 @@ int em28xx_start_analog_streaming(struct vb2_queue *vq, 
unsigned int count)
                em28xx_wake_i2c(dev);
 
                v4l2->capture_type = -1;
+               v4l2->last_field_id = -1;
                rc = em28xx_init_usb_xfer(dev, EM28XX_ANALOG_MODE,
                                          dev->analog_xfer_bulk,
                                          EM28XX_NUM_BUFS,
diff --git a/drivers/media/usb/em28xx/em28xx.h 
b/drivers/media/usb/em28xx/em28xx.h
index c916b159f73a..7f249ddf6179 100644
--- a/drivers/media/usb/em28xx/em28xx.h
+++ b/drivers/media/usb/em28xx/em28xx.h
@@ -606,6 +606,7 @@ struct em28xx_v4l2 {
        /* Capture state tracking */
        int capture_type;
        bool top_field;
+       int last_field_id;
        int vbi_read;
        unsigned int field_count;
 
_______________________________________________
linuxtv-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to