diff options
author | Thomas Wood <thomas.wood@intel.com> | 2013-10-16 10:58:50 -0400 |
---|---|---|
committer | Daniel Vetter <daniel.vetter@ffwll.ch> | 2013-10-16 13:47:37 -0400 |
commit | fbf460259bfecb3f99a33ba2aa30766c5f7e6e2c (patch) | |
tree | e9841f07155820f93c9f560056f0609bcff0699b | |
parent | 585a94b80ec2393fbb09739a355fb8ba9e7c27c4 (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.c | 103 |
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 | ||
2655 | static 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 | */ |
2664 | static int | 2708 | static int |
2665 | do_hdmi_vsdb_modes(struct drm_connector *connector, const u8 *db, u8 len) | 2709 | do_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 | ||
2705 | out: | 2786 | out: |
2706 | return modes; | 2787 | return modes; |
@@ -2759,8 +2840,8 @@ static int | |||
2759 | add_cea_modes(struct drm_connector *connector, struct edid *edid) | 2840 | add_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 | } |