diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index b7ee230..7eecfa0 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1522,21 +1522,102 @@ do_cea_modes (struct drm_connector *connector, u8 *db, u8 len)
 	return modes;
 }
 
+static bool cea_hdmi_3d_present(u8 *hdmi)
+{
+	u8 len, skip = 0;
+
+	len = hdmi[0] & 0x1f;
+
+	if (len < 8)
+		return false;
+
+	/* no HDMI_Video_present */
+	if (!(hdmi[8] & (1<<5)))
+		return false;
+
+	/* Latency_fields_present */
+	if (hdmi[8] & (1 << 7))
+		skip += 2;
+
+	/* I_Latency_fields_present */
+	if (hdmi[8] & (1 << 6))
+		skip += 2;
+
+	/* the declared length is not long enough */
+	if (len < (9 + skip))
+		return false;
+
+	return (hdmi[9 + skip] & (1 << 7)) != 0;
+}
+
+static const struct {
+	int width, height, freq;
+	unsigned int select, value;
+	unsigned int formats;
+} s3d_mandatory_modes[] = {
+	{ 1920, 1080, 24, DRM_MODE_FLAG_INTERLACE, 0,
+	  DRM_MODE_FLAG_3D_TOP_BOTTOM | DRM_MODE_FLAG_3D_FRAME_PACKING },
+	{ 1920, 1080, 50, DRM_MODE_FLAG_INTERLACE, DRM_MODE_FLAG_INTERLACE,
+	  DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
+	{ 1920, 1080, 60, DRM_MODE_FLAG_INTERLACE, DRM_MODE_FLAG_INTERLACE,
+	  DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF },
+	{ 1280, 720,  50, DRM_MODE_FLAG_INTERLACE, 0,
+	  DRM_MODE_FLAG_3D_TOP_BOTTOM | DRM_MODE_FLAG_3D_FRAME_PACKING },
+	{ 1280, 720,  60, DRM_MODE_FLAG_INTERLACE, 0,
+	  DRM_MODE_FLAG_3D_TOP_BOTTOM | DRM_MODE_FLAG_3D_FRAME_PACKING }
+};
+
+static void cea_hdmi_patch_mandatory_3d_mode(struct drm_display_mode *mode)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(s3d_mandatory_modes); i++) {
+		if (mode->hdisplay == s3d_mandatory_modes[i].width &&
+		    mode->vdisplay == s3d_mandatory_modes[i].height &&
+		    (mode->flags & s3d_mandatory_modes[i].select) ==
+				s3d_mandatory_modes[i].value &&
+		    drm_mode_vrefresh(mode) == s3d_mandatory_modes[i].freq) {
+			mode->flags |= s3d_mandatory_modes[i].formats;
+		}
+	}
+}
+
+static void cea_hdmi_patch_mandatory_3d_modes(struct drm_connector *connector)
+{
+	struct drm_display_mode *mode;
+
+	list_for_each_entry(mode, &connector->probed_modes, head)
+		cea_hdmi_patch_mandatory_3d_mode(mode);
+}
+
 static int
 add_cea_modes(struct drm_connector *connector, struct edid *edid)
 {
 	u8 * cea = drm_find_cea_extension(edid);
-	u8 * db, dbl;
-	int modes = 0;
+	u8 * db, *hdmi = NULL, dbl;
+	int modes = 0, vendor_id;
 
+	/* let's find the cea modes before looking at the hdmi vendor block
+	 * as the 3d_present flag needs to know about the supported modes
+	 * to infer the 3D modes */
 	if (cea && cea[1] >= 3) {
 		for (db = cea + 4; db < cea + cea[2]; db += dbl + 1) {
 			dbl = db[0] & 0x1f;
-			if (((db[0] & 0xe0) >> 5) == VIDEO_BLOCK)
+			switch ((db[0] & 0xe0) >> 5) {
+			case VIDEO_BLOCK:
 				modes += do_cea_modes (connector, db+1, dbl);
+				break;
+			case VENDOR_BLOCK:
+				vendor_id = db[1] | db[2] << 8 | db[3] << 16;
+				if (vendor_id == HDMI_IDENTIFIER)
+					hdmi = db;
+			}
 		}
 	}
 
+	if (connector->expose_3d_modes && hdmi && cea_hdmi_3d_present(hdmi))
+		cea_hdmi_patch_mandatory_3d_modes(connector);
+
 	return modes;
 }
 
diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
index 45b19c6..d5d22de 100644
--- a/include/drm/drm_mode.h
+++ b/include/drm/drm_mode.h
@@ -44,20 +44,27 @@
 
 /* Video mode flags */
 /* bit compatible with the xorg definitions. */
-#define DRM_MODE_FLAG_PHSYNC	(1<<0)
-#define DRM_MODE_FLAG_NHSYNC	(1<<1)
-#define DRM_MODE_FLAG_PVSYNC	(1<<2)
-#define DRM_MODE_FLAG_NVSYNC	(1<<3)
-#define DRM_MODE_FLAG_INTERLACE	(1<<4)
-#define DRM_MODE_FLAG_DBLSCAN	(1<<5)
-#define DRM_MODE_FLAG_CSYNC	(1<<6)
-#define DRM_MODE_FLAG_PCSYNC	(1<<7)
-#define DRM_MODE_FLAG_NCSYNC	(1<<8)
-#define DRM_MODE_FLAG_HSKEW	(1<<9) /* hskew provided */
-#define DRM_MODE_FLAG_BCAST	(1<<10)
-#define DRM_MODE_FLAG_PIXMUX	(1<<11)
-#define DRM_MODE_FLAG_DBLCLK	(1<<12)
-#define DRM_MODE_FLAG_CLKDIV2	(1<<13)
+#define DRM_MODE_FLAG_PHSYNC			(1<<0)
+#define DRM_MODE_FLAG_NHSYNC			(1<<1)
+#define DRM_MODE_FLAG_PVSYNC			(1<<2)
+#define DRM_MODE_FLAG_NVSYNC			(1<<3)
+#define DRM_MODE_FLAG_INTERLACE			(1<<4)
+#define DRM_MODE_FLAG_DBLSCAN			(1<<5)
+#define DRM_MODE_FLAG_CSYNC			(1<<6)
+#define DRM_MODE_FLAG_PCSYNC			(1<<7)
+#define DRM_MODE_FLAG_NCSYNC			(1<<8)
+#define DRM_MODE_FLAG_HSKEW			(1<<9) /* hskew provided */
+#define DRM_MODE_FLAG_BCAST			(1<<10)
+#define DRM_MODE_FLAG_PIXMUX			(1<<11)
+#define DRM_MODE_FLAG_DBLCLK			(1<<12)
+#define DRM_MODE_FLAG_CLKDIV2			(1<<13)
+#define DRM_MODE_FLAG_3D_TOP_BOTTOM		(1<<14)
+#define DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF	(1<<15)
+#define DRM_MODE_FLAG_3D_FRAME_PACKING		(1<<16)
+
+#define DRM_MODE_FLAG_3D_MASK	(DRM_MODE_FLAG_3D_TOP_BOTTOM |		\
+				 DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF |	\
+				 DRM_MODE_FLAG_3D_FRAME_PACKING)
 
 /* DPMS flags */
 /* bit compatible with the xorg definitions. */
