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.kavuri@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.kavuri@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;
On Fri, 2011-12-09 at 11:46 +0000, Kavuri, Sateesh wrote:
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 */
}
This reads poorly, which makes me think it's wrong. Is format supposed to be a bitfield or an enum?
+EXPORT_SYMBOL(drm_detect_3D_monitor);
I suspect this is poorly named. There exist 3D displays which are not HDMI.
+#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
i915 style is usually to write these as (1 << 22) I think.
More importantly, use semantic names for register contents. "Bit 17" doesn't mean anything.
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);
Well, which is it?
+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 */
+};
If format is supposed to be an enum, why aren't you using these symbolic values?
- ajax
Thanks for the comments. Fixed most of the issues with the earlier patch. Sending out a new one. Comments inline below.
-----Original Message----- From: Adam Jackson [mailto:ajax@redhat.com] Sent: Tuesday, December 13, 2011 9:51 PM To: Kavuri, Sateesh Cc: dri-devel@lists.freedesktop.org Subject: Re: [PATCH] drm: Enable reading 3D capabilities of 3D monitor
On Fri, 2011-12-09 at 11:46 +0000, Kavuri, Sateesh wrote:
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 */
}
This reads poorly, which makes me think it's wrong. Is format supposed to be a bitfield or an enum?
+EXPORT_SYMBOL(drm_detect_3D_monitor);
I suspect this is poorly named. There exist 3D displays which are not HDMI.
I want to integrate the other interfaces (eDP, DP, MIPI) panel detection in this method itself. Started off with implementing the HDMI part.
+#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
i915 style is usually to write these as (1 << 22) I think.
These I have fixed. Patch with these fixes below.
More importantly, use semantic names for register contents. "Bit 17" doesn't mean anything.
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);
Well, which is it?
+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 */
+};
If format is supposed to be an enum, why aren't you using these symbolic values?
I fixed the part to use a u16 integer value to represent the format.
- ajax
================ From d4c989bc807be730b2693a843fe93d4d559c05eb Mon Sep 17 00:00:00 2001 From: Sateesh Kavuri sateesh.kavuri@intel.com Date: Fri, 9 Dec 2011 17:08:42 +0530 Subject: [PATCH] Patch to enable reading the EDID information of a 3D capable HDMI monitor. This EDID information would be used to enable a user space application to switch the mode of the monitor to a specific 3D format.
-- Signed-off=by: sateesh.kavuri@intel.com --- drivers/gpu/drm/drm_edid.c | 109 ++++++++++++++++++++++++++++++++++++++++++++ include/drm/drm_crtc.h | 25 ++++++++++ 2 files changed, 134 insertions(+), 0 deletions(-)
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c index fe39c35..4fe49e8 100644 --- a/drivers/gpu/drm/drm_edid.c +++ b/drivers/gpu/drm/drm_edid.c @@ -1322,6 +1322,9 @@ add_detailed_modes(struct drm_connector *connector, struct edid *edid, #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. */ @@ -1563,6 +1566,110 @@ end: } EXPORT_SYMBOL(drm_detect_hdmi_monitor);
+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 + * + */ +struct drm_panel_3D_capabilities *drm_detect_3D_monitor(struct edid *edid) +{ + u8 *edid_ext; + int i, hdmi_id; + int start_offset, end_offset; + bool is_hdmi = false; + struct drm_panel_3D_capabilities *caps = 0; + + 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 = kmalloc(sizeof(struct drm_panel_3D_capabilities), GFP_KERNEL); + caps->panel_supports_3D = 1; + } + + /* 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] & (1 << 0)) + caps->format |= (1 << 0); /* FRAME_PACKING */ + if (edid_ext[i+15+hdmi_vic_len] & (1 << 1)) + caps->format |= (1 << 1); /*FIELD_ALTERNATIVE */ + if (edid_ext[i+15+hdmi_vic_len] & (1 << 2)) + caps->format |= (1 << 2); /* LINE_ALTERNATIVE */ + if (edid_ext[i+15+hdmi_vic_len] & 0x3) + caps->format |= (1 << 3); /*SIDE_BY_SIDE_FULL */ + if (edid_ext[i+15+hdmi_vic_len] & (1 << 4)) + caps->format |= (1 << 4); /* L_DEPTH */ + if (edid_ext[i+15+hdmi_vic_len] & 0x5) + caps->format |= (1 << 5); /* L_DEPTH_GFX_GFX_DEPTH */ + if (edid_ext[i+15+hdmi_vic_len] & 0x6) + caps->format |= (1 << 6); /* TOP_BOTTOM */ + if (edid_ext[i+14+hdmi_vic_len] & 0x7) + caps->format |= (1 << 7); /* S_BY_S_HALF */ + if (edid_ext[i+14+hdmi_vic_len] & (1 << 8)) + caps->format |= (1 << 8); /* S_BY_S_HALF_QUINCUNX */ + } + break; + } + } + +end: + return caps; +} +EXPORT_SYMBOL(drm_detect_3D_monitor); + /** * drm_detect_monitor_audio - check monitor audio capability * @@ -1630,6 +1737,7 @@ static void drm_add_display_info(struct edid *edid, /* driver figures it out in this case */ info->bpc = 0; info->color_formats = 0; + info->caps_3D = 0;
/* Only defined for 1.4 with digital displays */ if (edid->revision < 4) @@ -1675,6 +1783,7 @@ static void drm_add_display_info(struct edid *edid, return;
info->cea_rev = edid_ext[1]; + info->caps_3D= drm_detect_3D_monitor(edid); }
/** diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 8020798..adfae4c 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -186,6 +186,28 @@ enum subpixel_order { #define DRM_COLOR_FORMAT_RGB444 (1<<0) #define DRM_COLOR_FORMAT_YCRCB444 (1<<1) #define DRM_COLOR_FORMAT_YCRCB422 (1<<2) + +enum s3d_formats_ext { + HORIZONTAL_SUB_SAMPLING = 0x10 +}; + +/* Pre-defined 3D formats as per HDMI spec */ +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 */ +}; + +/* 3D capabilities */ +struct drm_panel_3D_capabilities { + u16 panel_supports_3D; + u16 format; +}; /* * Describes a given display (e.g. CRT or flat panel) and its limitations. */ @@ -208,6 +230,8 @@ struct drm_display_info { u8 cea_rev;
char *raw_edid; /* if any */ + + struct drm_panel_3D_capabilities *caps_3D; };
struct drm_framebuffer_funcs { @@ -798,6 +822,7 @@ 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 struct drm_panel_3D_capabilities *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,
On Wed, 2011-12-14 at 13:04 +0000, Kavuri, Sateesh wrote:
- /* Data block offset in CEA extension block */
- start_offset = 4;
- end_offset = edid_ext[2];
- /* 3D vars */
- int multi_present = 0;
Pretty sure kernel style frowns upon mixed decls and code.
- /*
* 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) ) {
This conditional will always be false:
if ((x == VENDOR_BLOCK) && ((y & 0x20) == 0x01))
I suspect you want:
#define MONITOR_VIDEO_PRESENT (1 << 6) ... if ((x == VENDOR_BLOCK) && (y & 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 = kmalloc(sizeof(struct drm_panel_3D_capabilities), GFP_KERNEL);
caps->panel_supports_3D = 1;
}
This will probably also not do what you want. Consider what happens if edid_ext[i+13] has (1 << 7) set.
/* Check if 3D_Multi_present is set */
if ((edid_ext[i+13] & 0x60) == 0x0) {
multi_present = true;
}
Code and comment disagree.
/* 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;
Note: multi_val can only have the values {0, 2, 4, 6} now.
if (multi_val == 0x0)
multi_present = NO_SPL_CAPS;
else if (multi_val == 0x1)
multi_present = STRUCTURE_PRESENT;
These two assignments (and the one above) are the only use of the multi_present variable, it's never used in a subsequent conditional or passed back to the caller.
The 'else' here can never be true, as noted above.
if ((multi_val == STRUCTURE_PRESENT) ||
(multi_val == STRUCTURE_MASK_PRESENT) ) {
if (edid_ext[i+15+hdmi_vic_len] & (1 << 0))
caps->format |= (1 << 0); /* FRAME_PACKING */
if (edid_ext[i+15+hdmi_vic_len] & (1 << 1))
caps->format |= (1 << 1); /*FIELD_ALTERNATIVE */
if (edid_ext[i+15+hdmi_vic_len] & (1 << 2))
caps->format |= (1 << 2); /* LINE_ALTERNATIVE */
if (edid_ext[i+15+hdmi_vic_len] & 0x3)
caps->format |= (1 << 3); /*SIDE_BY_SIDE_FULL */
if (edid_ext[i+15+hdmi_vic_len] & (1 << 4))
caps->format |= (1 << 4); /* L_DEPTH */
if (edid_ext[i+15+hdmi_vic_len] & 0x5)
caps->format |= (1 << 5); /* L_DEPTH_GFX_GFX_DEPTH */
if (edid_ext[i+15+hdmi_vic_len] & 0x6)
caps->format |= (1 << 6); /* TOP_BOTTOM */
if (edid_ext[i+14+hdmi_vic_len] & 0x7)
caps->format |= (1 << 7); /* S_BY_S_HALF */
if (edid_ext[i+14+hdmi_vic_len] & (1 << 8))
caps->format |= (1 << 8); /* S_BY_S_HALF_QUINCUNX */
}
Here you've made it clear that ->format is a bitfield, but the values should be in a header (so drivers can use them) and this code should only be using the symbolic names.
@@ -1675,6 +1783,7 @@ static void drm_add_display_info(struct edid *edid, return;
info->cea_rev = edid_ext[1];
- info->caps_3D= drm_detect_3D_monitor(edid);
}
/**
Whitespace.
+/* Pre-defined 3D formats as per HDMI spec */ +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 */
+};
These don't match the bit shifts you used earlier.
- ajax
dri-devel@lists.freedesktop.org