aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Vetter <daniel.vetter@ffwll.ch>2012-04-01 13:16:18 -0400
committerLuis Henriques <luis.henriques@canonical.com>2012-05-25 12:24:35 -0400
commit51a45c9f015d07c7a7a76a34e9bb346af7376611 (patch)
tree74f3c5c6b1744d730f410eb15703a8960422796f
parent0215bfcfb1e83633243885c6b4797d15ec3b05c1 (diff)
drm/i915: handle input/output sdvo timings separately in mode_set
BugLink: http://bugs.launchpad.net/bugs/996109 commit 6651819b4b4fc3caa6964c5d825eb4bb996f3905 upstream. We seem to have a decent confusion between the output timings and the input timings of the sdvo encoder. If I understand the code correctly, we use the original mode unchanged for the output timings, safe for the lvds case. And we should use the adjusted mode for input timings. Clarify the situation by adding an explicit output_dtd to the sdvo mode_set function and streamline the code-flow by moving the input and output mode setting in the sdvo encode together. Furthermore testing showed that the sdvo input timing needs the unadjusted dotclock, the sdvo chip will automatically compute the required pixel multiplier to get a dotclock above 100 MHz. Fix this up when converting a drm mode to an sdvo dtd. This regression was introduced in commit c74696b9c890074c1e1ee3d7496fc71eb3680ced Author: Pavel Roskin <proski@gnu.org> Date: Thu Sep 2 14:46:34 2010 -0400 i915: revert some checks added by commit 32aad86f particularly the following hunk: # diff --git a/drivers/gpu/drm/i915/intel_sdvo.c # b/drivers/gpu/drm/i915/intel_sdvo.c # index 093e914..62d22ae 100644 # --- a/drivers/gpu/drm/i915/intel_sdvo.c # +++ b/drivers/gpu/drm/i915/intel_sdvo.c # @@ -1122,11 +1123,9 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder, # # /* We have tried to get input timing in mode_fixup, and filled into # adjusted_mode */ # - if (intel_sdvo->is_tv || intel_sdvo->is_lvds) { # - intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); # + intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode); # + if (intel_sdvo->is_tv || intel_sdvo->is_lvds) # input_dtd.part2.sdvo_flags = intel_sdvo->sdvo_flags; # - } else # - intel_sdvo_get_dtd_from_mode(&input_dtd, mode); # # /* If it's a TV, we already set the output timing in mode_fixup. # * Otherwise, the output timing is equal to the input timing. Due to questions raised in review, below a more elaborate analysis of the bug at hand: Sdvo seems to have two timings, one is the output timing which will be sent over whatever is connected on the other side of the sdvo chip (panel, hdmi screen, tv), the other is the input timing which will be generated by the gmch pipe. It looks like sdvo is expected to scale between the two. To make things slightly more complicated, we have a bunch of special cases: - For lvds panel we always use a fixed output timing, namely intel_sdvo->sdvo_lvds_fixed_mode, hence that special case. - Sdvo has an interface to generate a preferred input timing for a given output timing. This is the confusing thing that I've tried to clear up with the follow-on patches. - A special requirement is that the input pixel clock needs to be between 100MHz and 200MHz (likely to keep it within the electromechanical design range of PCIe), 270MHz on later gen4+. Lower pixel clocks are doubled/quadrupled. The thing this patch tries to fix is that the pipe needs to be explicitly instructed to double/quadruple the pixels and needs the correspondingly higher pixel clock, whereas the sdvo adaptor seems to do that itself and needs the unadjusted pixel clock. For the sdvo encode side we already set the pixel mutliplier with a different command (0x21). This patch tries to fix this mess by: - Keeping the output mode timing in the unadjusted plain mode, safe for the lvds case. - Storing the input timing in the adjusted_mode with the adjusted pixel clock. This way we don't need to frob around with the core crtc mode set code. - Fixing up the pixelclock when constructing the sdvo dtd timing struct. This is why the first hunk of the patch is an integral part of the series. - Dropping the is_tv special case because input_dtd is equivalent to adjusted_mode after these changes. Follow-up patches clear this up further (by simply ripping out intel_sdvo->input_dtd because it's not needed). v2: Extend commit message with an in-depth bug analysis. Reported-and-Tested-by: Bernard Blackham <b-linuxgit@largestprime.net> Bugzilla: https://bugs.freedesktop.org/show_bug.cgi?id=48157 Reviewed-by: Jesse Barnes <jbarnes@virtuousgeek.org> Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/gpu/drm/i915/intel_sdvo.c34
1 files changed, 18 insertions, 16 deletions
diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
index bdda08e33c3..06bc46ee22f 100644
--- a/drivers/gpu/drm/i915/intel_sdvo.c
+++ b/drivers/gpu/drm/i915/intel_sdvo.c
@@ -724,6 +724,7 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
724 uint16_t width, height; 724 uint16_t width, height;
725 uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len; 725 uint16_t h_blank_len, h_sync_len, v_blank_len, v_sync_len;
726 uint16_t h_sync_offset, v_sync_offset; 726 uint16_t h_sync_offset, v_sync_offset;
727 int mode_clock;
727 728
728 width = mode->crtc_hdisplay; 729 width = mode->crtc_hdisplay;
729 height = mode->crtc_vdisplay; 730 height = mode->crtc_vdisplay;
@@ -738,7 +739,11 @@ static void intel_sdvo_get_dtd_from_mode(struct intel_sdvo_dtd *dtd,
738 h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start; 739 h_sync_offset = mode->crtc_hsync_start - mode->crtc_hblank_start;
739 v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start; 740 v_sync_offset = mode->crtc_vsync_start - mode->crtc_vblank_start;
740 741
741 dtd->part1.clock = mode->clock / 10; 742 mode_clock = mode->clock;
743 mode_clock /= intel_mode_get_pixel_multiplier(mode) ?: 1;
744 mode_clock /= 10;
745 dtd->part1.clock = mode_clock;
746
742 dtd->part1.h_active = width & 0xff; 747 dtd->part1.h_active = width & 0xff;
743 dtd->part1.h_blank = h_blank_len & 0xff; 748 dtd->part1.h_blank = h_blank_len & 0xff;
744 dtd->part1.h_high = (((width >> 8) & 0xf) << 4) | 749 dtd->part1.h_high = (((width >> 8) & 0xf) << 4) |
@@ -990,7 +995,7 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
990 struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder); 995 struct intel_sdvo *intel_sdvo = to_intel_sdvo(encoder);
991 u32 sdvox; 996 u32 sdvox;
992 struct intel_sdvo_in_out_map in_out; 997 struct intel_sdvo_in_out_map in_out;
993 struct intel_sdvo_dtd input_dtd; 998 struct intel_sdvo_dtd input_dtd, output_dtd;
994 int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode); 999 int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
995 int rate; 1000 int rate;
996 1001
@@ -1015,20 +1020,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
1015 intel_sdvo->attached_output)) 1020 intel_sdvo->attached_output))
1016 return; 1021 return;
1017 1022
1018 /* We have tried to get input timing in mode_fixup, and filled into 1023 /* lvds has a special fixed output timing. */
1019 * adjusted_mode. 1024 if (intel_sdvo->is_lvds)
1020 */ 1025 intel_sdvo_get_dtd_from_mode(&output_dtd,
1021 if (intel_sdvo->is_tv || intel_sdvo->is_lvds) { 1026 intel_sdvo->sdvo_lvds_fixed_mode);
1022 input_dtd = intel_sdvo->input_dtd; 1027 else
1023 } else { 1028 intel_sdvo_get_dtd_from_mode(&output_dtd, mode);
1024 /* Set the output timing to the screen */ 1029 (void) intel_sdvo_set_output_timing(intel_sdvo, &output_dtd);
1025 if (!intel_sdvo_set_target_output(intel_sdvo,
1026 intel_sdvo->attached_output))
1027 return;
1028
1029 intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
1030 (void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd);
1031 }
1032 1030
1033 /* Set the input timing to the screen. Assume always input 0. */ 1031 /* Set the input timing to the screen. Assume always input 0. */
1034 if (!intel_sdvo_set_target_input(intel_sdvo)) 1032 if (!intel_sdvo_set_target_input(intel_sdvo))
@@ -1046,6 +1044,10 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
1046 !intel_sdvo_set_tv_format(intel_sdvo)) 1044 !intel_sdvo_set_tv_format(intel_sdvo))
1047 return; 1045 return;
1048 1046
1047 /* We have tried to get input timing in mode_fixup, and filled into
1048 * adjusted_mode.
1049 */
1050 intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
1049 (void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd); 1051 (void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd);
1050 1052
1051 switch (pixel_multiplier) { 1053 switch (pixel_multiplier) {