This is first of the patches to enable reading the 3D capabilities of a 
connected monitor on
HDMI. Similar functionality would also be implemented for other interface, eDP, 
DP

The idea is to read the 3D capabilities via EDID and provide this to a user 
space application
that would want to switch the mode of the connected monitor to a specific 3D 
format. The
implementation follows the HDMI 1.4a specification.

>From 95dc9536dc1a863a4a2d12836ead338835a4be59 Mon Sep 17 00:00:00 2001
From: Sateesh Kavuri <sateesh.kav...@intel.com>
Date: Wed, 7 Dec 2011 14:49:20 +0530
Subject: [PATCH] This patch enables the reading of the 3D capabilities of the 
connected
 HDMI monitor. Currently only the 3D format support fields are read.
 Later this could be extended. This information is expected to be sent to
 the user space which then sets the format on the monitor.
 This patch also handles DIP register signaling to the HDMI connected 3D
 monitor the 3D format to be set

---
Signed-off-by: Sateesh Kavuri <sateesh.kav...@intel.com>
---
---
 drivers/gpu/drm/drm_edid.c        |  109 +++++++++++++++++++++++++++++++++
 drivers/gpu/drm/i915/intel_drv.h  |   11 ++++
 drivers/gpu/drm/i915/intel_hdmi.c |  119 +++++++++++++++++++++++++++++++++++++
 include/drm/drm_crtc.h            |    2 +
 include/drm/drm_edid.h            |   20 ++++++
 5 files changed, 261 insertions(+), 0 deletions(-)

diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index fe39c35..5d4a425 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -1321,6 +1321,8 @@ add_detailed_modes(struct drm_connector *connector, 
struct edid *edid,
 #define VENDOR_BLOCK    0x03
 #define SPEAKER_BLOCK  0x04
 #define EDID_BASIC_AUDIO       (1 << 6)
+#define MONITOR_VIDEO_PRESENT  0x01
+#define PANEL_SUPPORTS_3D      0x01
 
 /**
  * Search EDID for CEA extension block.
@@ -1610,6 +1612,113 @@ end:
 }
 EXPORT_SYMBOL(drm_detect_monitor_audio);
 
+enum {
+       NO_SPL_CAPS = 0x0,
+       STRUCTURE_PRESENT = 0x1,
+       STRUCTURE_MASK_PRESENT = 0x2
+} are_3d_caps_present;
+
+/**
+ * drm_detect_monitor_3D - check monitor 3D capabilities
+ *
+ * Monitor should have CEA extension block.
+ * Check if the monitor has 3D capabilities. If it does, store the capabilitie
+ * to a data structure
+ *
+ * FIXME: Currently returning bool (to denote 3D present or not), later should
+ * return the 3D capabilities (or can there be a separate function for that?)
+ */
+bool drm_detect_3D_monitor(struct edid *edid)
+{
+       u8 *edid_ext;
+       int i, hdmi_id;
+       int start_offset, end_offset;
+       bool is_hdmi = false;
+       bool is_3d = false;
+       struct panel_3d_caps *caps = kmalloc(sizeof(struct panel_3d_caps), 
GFP_KERNEL);
+
+       edid_ext = drm_find_cea_extension(edid);
+       if (!edid_ext)
+               goto end;
+
+       /* Data block offset in CEA extension block */
+       start_offset = 4;
+       end_offset = edid_ext[2];
+
+       /* 3D vars */
+       int multi_present = 0;
+
+       /*
+        * Because HDMI identifier is in Vendor Specific Block,
+        * search it from all data blocks of CEA extension.
+        */
+       for (i = start_offset; i < end_offset;
+               /* Increased by data block len */
+               i += ((edid_ext[i] & 0x1f) + 1)) {
+               /* Find vendor specific block */
+               /* 6'th bit is the VIDEO_PRESENT bit */
+               if ( ((edid_ext[i] >> 5) == VENDOR_BLOCK) &&
+                    ((edid_ext[i+8] & 0x20) == MONITOR_VIDEO_PRESENT) ) {
+                       hdmi_id = edid_ext[i + 1] | (edid_ext[i + 2] << 8) |
+                                 edid_ext[i + 3] << 16;
+                       /* Find HDMI identifier */
+                       if (hdmi_id == HDMI_IDENTIFIER)
+                               is_hdmi = true;
+
+                       /* Check for the 3D_Present flag */
+                       if ((edid_ext[i+13] >> 6) == PANEL_SUPPORTS_3D) {
+                               caps->panel_supports_3d = 1;
+                               is_3d = true;
+                       }
+
+                       /* Check if 3D_Multi_present is set */
+                       if ((edid_ext[i+13] & 0x60) == 0x0) {
+                               multi_present = true;
+                       }
+
+                       /* Collect 3D capabilities of the monitor */
+                       int hdmi_3d_len = 0;
+                       int hdmi_vic_len = 0;
+                       hdmi_vic_len = (edid_ext[i+14] >> 0x05);
+                       hdmi_3d_len = ((edid_ext[i+14] << 0x03) >>0x03);
+                       int multi_val = edid_ext[i+13] & 0x6;
+                       if (multi_val == 0x0)
+                               multi_present = NO_SPL_CAPS;
+                       else if (multi_val == 0x1)
+                               multi_present = STRUCTURE_PRESENT;
+                       else if (multi_val == 0x2)
+                               multi_val = STRUCTURE_MASK_PRESENT;
+
+                       if ((multi_val == STRUCTURE_PRESENT) || 
+                               (multi_val == STRUCTURE_MASK_PRESENT) ) {
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x01) == 
0x01)
+                                       caps->format |= 0x0; /* FRAME_PACKING */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x02) == 
0x02)
+                                       caps->format |= 0x1; 
/*FIELD_ALTERNATIVE */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x04) == 
0x04)
+                                       caps->format |= 0x2; /* 
LINE_ALTERNATIVE */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x08) == 
0x08)
+                                       caps->format |= 0x3; 
/*SIDE_BY_SIDE_FULL */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x10) == 
0x10)
+                                       caps->format |= 0x4; /* L_DEPTH */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x20) == 
0x20)
+                                       caps->format |= 0x5; /* 
L_DEPTH_GFX_GFX_DEPTH */
+                               if ((edid_ext[i+15+hdmi_vic_len] & 0x40) == 
0x40)
+                                       caps->format |= 0x6; /* TOP_BOTTOM */
+                               if ((edid_ext[i+14+hdmi_vic_len] & 0x01) == 
0x01)
+                                       caps->format |= 0x7; /* S_BY_S_HALF */
+                               if ((edid_ext[i+14+hdmi_vic_len] & 0x80) == 
0x80)
+                                       caps->format |= 0x8; /* 
S_BY_S_HALF_QUINCUNX */
+                       }
+                       break; 
+               }
+       }
+
+end:
+       return is_3d;
+}
+EXPORT_SYMBOL(drm_detect_3D_monitor);
+
 /**
  * drm_add_display_info - pull display info out if present
  * @edid: EDID data
diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
index bd9a604..2edfdc4 100644
--- a/drivers/gpu/drm/i915/intel_drv.h
+++ b/drivers/gpu/drm/i915/intel_drv.h
@@ -183,6 +183,11 @@ struct intel_crtc {
 
 #define DIP_HEADER_SIZE        5
 
+/* Vendor info frame = 0x81 */
+#define DIP_TYPE_VSIF    0x81
+#define DIP_VERSION_VSIF    0x01
+#define DIP_LEN_VSIF     06
+
 #define DIP_TYPE_AVI    0x82
 #define DIP_VERSION_AVI 0x2
 #define DIP_LEN_AVI     13
@@ -232,6 +237,12 @@ struct dip_infoframe {
                        uint8_t pd[16];
                        uint8_t sdi;
                } spd;
+               struct {
+                        uint8_t ieee[3];
+                        uint8_t hdmi_video_format; /* last 3 bits are used */
+                        uint8_t s3d_struct;         /* last 4 bits are used */
+                        uint8_t s3d_struct_ext;     /* last 4 bits are used */
+                } hdmi_vendor_info;
                uint8_t payload[27];
        } __attribute__ ((packed)) body;
 } __attribute__((packed));
diff --git a/drivers/gpu/drm/i915/intel_hdmi.c 
b/drivers/gpu/drm/i915/intel_hdmi.c
index d4f5a0b..db1d196 100644
--- a/drivers/gpu/drm/i915/intel_hdmi.c
+++ b/drivers/gpu/drm/i915/intel_hdmi.c
@@ -44,6 +44,7 @@ struct intel_hdmi {
        uint32_t color_range;
        bool has_hdmi_sink;
        bool has_audio;
+       bool has_3D;
        int force_audio;
        void (*write_infoframe)(struct drm_encoder *encoder,
                                struct dip_infoframe *frame);
@@ -86,6 +87,9 @@ static u32 intel_infoframe_index(struct dip_infoframe *frame)
        case DIP_TYPE_SPD:
                flags |= VIDEO_DIP_SELECT_SPD;
                break;
+       case DIP_TYPE_VSIF:
+               flags |= VIDEO_DIP_SELECT_VENDOR;
+               break;
        default:
                DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
                break;
@@ -105,6 +109,9 @@ static u32 intel_infoframe_flags(struct dip_infoframe 
*frame)
        case DIP_TYPE_SPD:
                flags |= VIDEO_DIP_ENABLE_SPD | VIDEO_DIP_FREQ_VSYNC;
                break;
+       case DIP_TYPE_VSIF:
+               flags |= VIDEO_DIP_ENABLE_VENDOR | VIDEO_DIP_FREQ_VSYNC;
+               break;
        default:
                DRM_DEBUG_DRIVER("unknown info frame type %d\n", frame->type);
                break;
@@ -200,6 +207,117 @@ static void intel_hdmi_set_avi_infoframe(struct 
drm_encoder *encoder)
        intel_set_infoframe(encoder, &avi_if);
 }
 
+#define VSIF_RESET_INDEX 0xFFFFFFF8
+#define VSIF_RESET_BIT_22 0xFFBFFFFF
+#define VSIF_SET_BIT_19 0x80000
+#define VSIF_RESET_BIT_20 0xFFEFFFFF
+#define VSIF_RESET_BIT_17 0x10000
+#define VSIF_SET_BIT_16 0xFFFDFFFF
+#define VSIF_SET_MASTER_BIT 0x400000
+
+/*
+ * write_vendor_infoframe
+ * Writes the vendor info frame
+ */
+static void write_vendor_infoframe(struct drm_encoder *encoder,
+                                    struct dip_infoframe *frame)
+{
+
+       uint32_t *data = (uint32_t *)frame;
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       unsigned i, len = DIP_HEADER_SIZE + frame->len;
+       u32 val = I915_READ(reg);
+
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+       /* set bits 0..3 = 0, i.e., reset the index of the data register */
+       val &= VSIF_RESET_INDEX;
+
+       /* Set bit 22=0 */
+       val &= VSIF_RESET_BIT_22;
+       /* Set bit 19=1 */
+       val |= VSIF_SET_BIT_19;
+
+       /* Set bit 20=0 */
+       val &= VSIF_RESET_BIT_20;
+
+       /* Set bits 17,16 = 01 */
+       val |= VSIF_RESET_BIT_17;
+       val &= VSIF_SET_BIT_16;
+
+        I915_WRITE(reg, val );
+
+       for (i = 0; i < len; i += 4) {
+               I915_WRITE(reg, *data);
+               data++;
+       }
+       val = I915_READ(reg);
+
+       /* Set bit 22=1 */
+       val |= VSIF_SET_MASTER_BIT;
+ 
+       I915_WRITE(reg, val );
+}
+
+#define HDMI_REG_ID_1 0x03
+#define HDMI_REG_ID_2 0x0C
+#define HDMI_REG_ID_3 0x00
+#define HDMI_3D_VIDEO_FORMAT 0x40
+
+/**
+ * intel_hdmi_set_vendor_infoframe
+ * Sets the vendor info frame for a particular 3D format
+ */
+static void intel_hdmi_set_vendor_infoframe(struct drm_encoder *encoder,
+                                       enum s3d_formats format,
+                                       enum s3d_formats_ext subformat)
+{
+       struct dip_infoframe hdmi_vendor_if;
+       hdmi_vendor_if.type = DIP_TYPE_VSIF;
+       hdmi_vendor_if.ver = DIP_VERSION_VSIF;
+       hdmi_vendor_if.len = DIP_LEN_VSIF;
+       hdmi_vendor_if.body.hdmi_vendor_info.ieee[0] = HDMI_REG_ID_1;
+       hdmi_vendor_if.body.hdmi_vendor_info.ieee[1] = HDMI_REG_ID_2;
+       hdmi_vendor_if.body.hdmi_vendor_info.ieee[2] = HDMI_REG_ID_3;
+       hdmi_vendor_if.body.hdmi_vendor_info.hdmi_video_format = 
HDMI_3D_VIDEO_FORMAT;
+       /* 0x80 for side-by-side half */
+       hdmi_vendor_if.body.hdmi_vendor_info.s3d_struct = format;
+       /* 0x10 for horizontal sub-sampling */
+       hdmi_vendor_if.body.hdmi_vendor_info.s3d_struct_ext = subformat;
+
+       struct intel_hdmi *intel_hdmi = enc_to_intel_hdmi(encoder);
+
+        if (!intel_hdmi->has_hdmi_sink)
+                return;
+
+        intel_dip_infoframe_csum(&hdmi_vendor_if);
+       write_vendor_infoframe(encoder, &hdmi_vendor_if);
+
+}
+
+/**
+ * Disable s3d by knocking out the vendor info frame
+ */
+static void disable_vendor_info_frame(struct drm_encoder *encoder)
+{
+       struct drm_device *dev = encoder->dev;
+       struct drm_i915_private *dev_priv = dev->dev_private;
+       struct drm_crtc *crtc = encoder->crtc;
+       struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
+       int reg = TVIDEO_DIP_CTL(intel_crtc->pipe);
+       u32 val = I915_READ(reg);
+
+       intel_wait_for_vblank(dev, intel_crtc->pipe);
+
+       val &= ~VIDEO_DIP_ENABLE_VENDOR;
+
+       I915_WRITE(reg, val);
+}
+
 static void intel_hdmi_set_spd_infoframe(struct drm_encoder *encoder)
 {
        struct dip_infoframe spd_if;
@@ -337,6 +455,7 @@ intel_hdmi_detect(struct drm_connector *connector, bool 
force)
                        status = connector_status_connected;
                        intel_hdmi->has_hdmi_sink = 
drm_detect_hdmi_monitor(edid);
                        intel_hdmi->has_audio = drm_detect_monitor_audio(edid);
+                       intel_hdmi->has_3D = drm_detect_3D_monitor(edid);
                }
                connector->display_info.raw_edid = NULL;
                kfree(edid);
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index 8020798..5b4d09c 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -798,6 +798,8 @@ extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
 extern u8 *drm_find_cea_extension(struct edid *edid);
 extern bool drm_detect_hdmi_monitor(struct edid *edid);
 extern bool drm_detect_monitor_audio(struct edid *edid);
+extern bool drm_detect_3D_monitor(struct edid *edid);
+//extern struct panel_3d_caps* drm_detect_3D_monitor(struct edid *edid);
 extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
                                    void *data, struct drm_file *file_priv);
 extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
index 74ce916..06445e6 100644
--- a/include/drm/drm_edid.h
+++ b/include/drm/drm_edid.h
@@ -230,6 +230,26 @@ struct edid {
 
 #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
 
+enum s3d_formats {
+       FRAME_PACKING           = 0x0,
+       FIELD_ALTERNATIVE       = 0x1,
+       LINE_ALTERNATIVE        = 0x2,
+       SIDE_BY_SIDE_FULL       = 0x3,
+       L_DEPTH                 = 0x4,
+       L_DEPTH_GFX_GFX_DEPTH   = 0x5,
+       TOP_BOTTOM              = 0x6, /* 0x7 is reserved for future */
+       SIDE_BY_SIDE_HALF       = 0x8  /* 0x9 onwards is reserved for future */
+};
+
+enum s3d_formats_ext {
+       HORIZONTAL_SUB_SAMPLING = 0x10
+};
+
+struct panel_3d_caps {
+       u16     panel_supports_3d;
+       u16     format;
+};
+
 struct drm_encoder;
 struct drm_connector;
 struct drm_display_mode;
-- 
1.7.4.1

_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
http://lists.freedesktop.org/mailman/listinfo/dri-devel

Reply via email to