aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wood <thomas.wood@intel.com>2013-10-16 10:58:50 -0400
committerDaniel Vetter <daniel.vetter@ffwll.ch>2013-10-16 13:47:37 -0400
commitfbf460259bfecb3f99a33ba2aa30766c5f7e6e2c (patch)
treee9841f07155820f93c9f560056f0609bcff0699b
parent585a94b80ec2393fbb09739a355fb8ba9e7c27c4 (diff)
drm: add support for additional stereo 3D modes
Parse the 3D_Structure_ALL and 3D_MASK fields of the HDMI Vendor Specific Data Block to expose more stereo 3D modes. v2: Use (1 << 0) for consistency. (Ville Syrjälä) Skip adding any modes if 3D_MASK is indicated as being present but the length only includes 3D_Structure_ALL. (Ville Syrjälä) Check that the value of HDMI_3D_LEN is large enough to include 3D_Structure_ALL and 3D_MASK, if they are present. (Ville Syrjälä) v3: Increment offset before the length checks. (Ville Syrjälä) Signed-off-by: Thomas Wood <thomas.wood@intel.com> Reviewed-by: Ville Syrjälä <ville.syrjala@linux.intel.com> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
-rw-r--r--drivers/gpu/drm/drm_edid.c103
1 files changed, 94 insertions, 9 deletions
diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
index 9e81609b1e29..f1764ec5818b 100644
--- a/drivers/gpu/drm/drm_edid.c
+++ b/drivers/gpu/drm/drm_edid.c
@@ -2652,6 +2652,50 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
2652 return 1; 2652 return 1;
2653} 2653}
2654 2654
2655static int add_3d_struct_modes(struct drm_connector *connector, u16 structure,
2656 const u8 *video_db, u8 video_len, u8 video_index)
2657{
2658 struct drm_device *dev = connector->dev;
2659 struct drm_display_mode *newmode;
2660 int modes = 0;
2661 u8 cea_mode;
2662
2663 if (video_db == NULL || video_index > video_len)
2664 return 0;
2665
2666 /* CEA modes are numbered 1..127 */
2667 cea_mode = (video_db[video_index] & 127) - 1;
2668 if (cea_mode >= ARRAY_SIZE(edid_cea_modes))
2669 return 0;
2670
2671 if (structure & (1 << 0)) {
2672 newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
2673 if (newmode) {
2674 newmode->flags |= DRM_MODE_FLAG_3D_FRAME_PACKING;
2675 drm_mode_probed_add(connector, newmode);
2676 modes++;
2677 }
2678 }
2679 if (structure & (1 << 6)) {
2680 newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
2681 if (newmode) {
2682 newmode->flags |= DRM_MODE_FLAG_3D_TOP_AND_BOTTOM;
2683 drm_mode_probed_add(connector, newmode);
2684 modes++;
2685 }
2686 }
2687 if (structure & (1 << 8)) {
2688 newmode = drm_mode_duplicate(dev, &edid_cea_modes[cea_mode]);
2689 if (newmode) {
2690 newmode->flags = DRM_MODE_FLAG_3D_SIDE_BY_SIDE_HALF;
2691 drm_mode_probed_add(connector, newmode);
2692 modes++;
2693 }
2694 }
2695
2696 return modes;
2697}
2698
2655/* 2699/*
2656 * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block 2700 * do_hdmi_vsdb_modes - Parse the HDMI Vendor Specific data block
2657 * @connector: connector corresponding to the HDMI sink 2701 * @connector: connector corresponding to the HDMI sink
@@ -2662,10 +2706,13 @@ static int add_hdmi_mode(struct drm_connector *connector, u8 vic)
2662 * also adds the stereo 3d modes when applicable. 2706 * also adds the stereo 3d modes when applicable.
2663 */ 2707 */
2664static int 2708static int
2665do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) 2709do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len,
2710 const u8 *video_db, u8 video_len)
2666{ 2711{
2667 int modes = 0, offset = 0, i; 2712 int modes = 0, offset = 0, i, multi_present = 0;
2668 u8 vic_len; 2713 u8 vic_len, hdmi_3d_len = 0;
2714 u16 mask;
2715 u16 structure_all;
2669 2716
2670 if (len < 8) 2717 if (len < 8)
2671 goto out; 2718 goto out;
@@ -2689,11 +2736,16 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
2689 2736
2690 /* 3D_Present */ 2737 /* 3D_Present */
2691 offset++; 2738 offset++;
2692 if (db[8 + offset] & (1 << 7)) 2739 if (db[8 + offset] & (1 << 7)) {
2693 modes += add_hdmi_mandatory_stereo_modes(connector); 2740 modes += add_hdmi_mandatory_stereo_modes(connector);
2694 2741
2742 /* 3D_Multi_present */
2743 multi_present = (db[8 + offset] & 0x60) >> 5;
2744 }
2745
2695 offset++; 2746 offset++;
2696 vic_len = db[8 + offset] >> 5; 2747 vic_len = db[8 + offset] >> 5;
2748 hdmi_3d_len = db[8 + offset] & 0x1f;
2697 2749
2698 for (i = 0; i < vic_len && len >= (9 + offset + i); i++) { 2750 for (i = 0; i < vic_len && len >= (9 + offset + i); i++) {
2699 u8 vic; 2751 u8 vic;
@@ -2701,6 +2753,35 @@ do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len)
2701 vic = db[9 + offset + i]; 2753 vic = db[9 + offset + i];
2702 modes += add_hdmi_mode(connector, vic); 2754 modes += add_hdmi_mode(connector, vic);
2703 } 2755 }
2756 offset += 1 + vic_len;
2757
2758 if (!(multi_present == 1 || multi_present == 2))
2759 goto out;
2760
2761 if ((multi_present == 1 && len < (9 + offset)) ||
2762 (multi_present == 2 && len < (11 + offset)))
2763 goto out;
2764
2765 if ((multi_present == 1 && hdmi_3d_len < 2) ||
2766 (multi_present == 2 && hdmi_3d_len < 4))
2767 goto out;
2768
2769 /* 3D_Structure_ALL */
2770 structure_all = (db[8 + offset] << 8) | db[9 + offset];
2771
2772 /* check if 3D_MASK is present */
2773 if (multi_present == 2)
2774 mask = (db[10 + offset] << 8) | db[11 + offset];
2775 else
2776 mask = 0xffff;
2777
2778 for (i = 0; i < 16; i++) {
2779 if (mask & (1 << i))
2780 modes += add_3d_struct_modes(connector,
2781 structure_all,
2782 video_db,
2783 video_len, i);
2784 }
2704 2785
2705out: 2786out:
2706 return modes; 2787 return modes;
@@ -2759,8 +2840,8 @@ static int
2759add_cea_modes(struct drm_connector *connector, struct edid *edid) 2840add_cea_modes(struct drm_connector *connector, struct edid *edid)
2760{ 2841{
2761 const u8 *cea = drm_find_cea_extension(edid); 2842 const u8 *cea = drm_find_cea_extension(edid);
2762 const u8 *db, *hdmi = NULL; 2843 const u8 *db, *hdmi = NULL, *video = NULL;
2763 u8 dbl, hdmi_len; 2844 u8 dbl, hdmi_len, video_len = 0;
2764 int modes = 0; 2845 int modes = 0;
2765 2846
2766 if (cea && cea_revision(cea) >= 3) { 2847 if (cea && cea_revision(cea) >= 3) {
@@ -2773,8 +2854,11 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
2773 db = &cea[i]; 2854 db = &cea[i];
2774 dbl = cea_db_payload_len(db); 2855 dbl = cea_db_payload_len(db);
2775 2856
2776 if (cea_db_tag(db) == VIDEO_BLOCK) 2857 if (cea_db_tag(db) == VIDEO_BLOCK) {
2777 modes += do_cea_modes(connector, db + 1, dbl); 2858 video = db + 1;
2859 video_len = dbl;
2860 modes += do_cea_modes(connector, video, dbl);
2861 }
2778 else if (cea_db_is_hdmi_vsdb(db)) { 2862 else if (cea_db_is_hdmi_vsdb(db)) {
2779 hdmi = db; 2863 hdmi = db;
2780 hdmi_len = dbl; 2864 hdmi_len = dbl;
@@ -2787,7 +2871,8 @@ add_cea_modes(struct drm_connector *connector, struct edid *edid)
2787 * be patching their flags when the sink supports stereo 3D. 2871 * be patching their flags when the sink supports stereo 3D.
2788 */ 2872 */
2789 if (hdmi) 2873 if (hdmi)
2790 modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len); 2874 modes += do_hdmi_vsdb_modes(connector, hdmi, hdmi_len, video,
2875 video_len);
2791 2876
2792 return modes; 2877 return modes;
2793} 2878}