drm/i915/sdvo: Preserve pixel-multiplier

Store the pixel-multiplier on the adjusted mode and avoid modifying the
requested mode.

Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
This commit is contained in:
Chris Wilson 2010-08-25 10:05:17 +01:00
parent 57cd6508da
commit 6c9547ff35
3 changed files with 75 additions and 67 deletions

View File

@ -3519,7 +3519,6 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
int trans_dpll_sel = (pipe == 0) ? 0 : 1; int trans_dpll_sel = (pipe == 0) ? 0 : 1;
int lvds_reg = LVDS; int lvds_reg = LVDS;
u32 temp; u32 temp;
int sdvo_pixel_multiply;
int target_clock; int target_clock;
drm_vblank_pre_modeset(dev, pipe); drm_vblank_pre_modeset(dev, pipe);
@ -3770,12 +3769,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
else else
dpll |= DPLLB_MODE_DAC_SERIAL; dpll |= DPLLB_MODE_DAC_SERIAL;
if (is_sdvo) { if (is_sdvo) {
int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
if (pixel_multiplier > 1) {
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dpll |= (pixel_multiplier - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
else if (HAS_PCH_SPLIT(dev))
dpll |= (pixel_multiplier - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
}
dpll |= DPLL_DVO_HIGH_SPEED; dpll |= DPLL_DVO_HIGH_SPEED;
sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
else if (HAS_PCH_SPLIT(dev))
dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
} }
if (is_dp) if (is_dp)
dpll |= DPLL_DVO_HIGH_SPEED; dpll |= DPLL_DVO_HIGH_SPEED;
@ -3982,9 +3983,15 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) { if (IS_I965G(dev) && !HAS_PCH_SPLIT(dev)) {
if (is_sdvo) { if (is_sdvo) {
sdvo_pixel_multiply = adjusted_mode->clock / mode->clock; int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) | if (pixel_multiplier > 1)
((sdvo_pixel_multiply - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT)); pixel_multiplier = (pixel_multiplier - 1) << DPLL_MD_UDI_MULTIPLIER_SHIFT;
else
pixel_multiplier = 0;
I915_WRITE(dpll_md_reg,
(0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
pixel_multiplier);
} else } else
I915_WRITE(dpll_md_reg, 0); I915_WRITE(dpll_md_reg, 0);
} else { } else {

View File

@ -99,6 +99,24 @@
#define INTEL_DVO_CHIP_TMDS 2 #define INTEL_DVO_CHIP_TMDS 2
#define INTEL_DVO_CHIP_TVOUT 4 #define INTEL_DVO_CHIP_TVOUT 4
/* drm_display_mode->private_flags */
#define INTEL_MODE_PIXEL_MULTIPLIER_SHIFT (0x0)
#define INTEL_MODE_PIXEL_MULTIPLIER_MASK (0xf << INTEL_MODE_PIXEL_MULTIPLIER_SHIFT)
static inline void
intel_mode_set_pixel_multiplier(struct drm_display_mode *mode,
int multiplier)
{
mode->clock *= multiplier;
mode->private_flags |= multiplier;
}
static inline int
intel_mode_get_pixel_multiplier(const struct drm_display_mode *mode)
{
return (mode->private_flags & INTEL_MODE_PIXEL_MULTIPLIER_MASK) >> INTEL_MODE_PIXEL_MULTIPLIER_SHIFT;
}
struct intel_i2c_chan { struct intel_i2c_chan {
struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */ struct drm_device *drm_dev; /* for getting at dev. private (mmio etc.) */
u32 reg; /* GPIO reg */ u32 reg; /* GPIO reg */

View File

@ -106,15 +106,11 @@ struct intel_sdvo {
bool is_hdmi; bool is_hdmi;
/** /**
* This is set if we detect output of sdvo device as LVDS. * This is set if we detect output of sdvo device as LVDS and
* have a valid fixed mode to use with the panel.
*/ */
bool is_lvds; bool is_lvds;
/**
* This is sdvo flags for input timing.
*/
uint8_t sdvo_flags;
/** /**
* This is sdvo fixed pannel mode pointer * This is sdvo fixed pannel mode pointer
*/ */
@ -132,6 +128,8 @@ struct intel_sdvo {
/* Mac mini hack -- use the same DDC as the analog connector */ /* Mac mini hack -- use the same DDC as the analog connector */
struct i2c_adapter *analog_ddc_bus; struct i2c_adapter *analog_ddc_bus;
/* Input timings for adjusted_mode */
struct intel_sdvo_dtd input_dtd;
}; };
struct intel_sdvo_connector { struct intel_sdvo_connector {
@ -1022,8 +1020,6 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
struct drm_display_mode *mode, struct drm_display_mode *mode,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
{ {
struct intel_sdvo_dtd input_dtd;
/* Reset the input timing to the screen. Assume always input 0. */ /* Reset the input timing to the screen. Assume always input 0. */
if (!intel_sdvo_set_target_input(intel_sdvo)) if (!intel_sdvo_set_target_input(intel_sdvo))
return false; return false;
@ -1035,14 +1031,12 @@ intel_sdvo_set_input_timings_for_mode(struct intel_sdvo *intel_sdvo,
return false; return false;
if (!intel_sdvo_get_preferred_input_timing(intel_sdvo, if (!intel_sdvo_get_preferred_input_timing(intel_sdvo,
&input_dtd)) &intel_sdvo->input_dtd))
return false; return false;
intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd); intel_sdvo_get_mode_from_dtd(adjusted_mode, &intel_sdvo->input_dtd);
intel_sdvo->sdvo_flags = input_dtd.part2.sdvo_flags;
drm_mode_set_crtcinfo(adjusted_mode, 0); drm_mode_set_crtcinfo(adjusted_mode, 0);
mode->clock = adjusted_mode->clock;
return true; return true;
} }
@ -1051,6 +1045,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
struct drm_display_mode *adjusted_mode) struct drm_display_mode *adjusted_mode)
{ {
struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
int multiplier;
/* We need to construct preferred input timings based on our /* We need to construct preferred input timings based on our
* output timings. To do that, we have to set the output * output timings. To do that, we have to set the output
@ -1065,10 +1060,8 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
mode, mode,
adjusted_mode); adjusted_mode);
} else if (intel_sdvo->is_lvds) { } else if (intel_sdvo->is_lvds) {
drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode, 0);
if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo, if (!intel_sdvo_set_output_timings_from_mode(intel_sdvo,
intel_sdvo->sdvo_lvds_fixed_mode)) intel_sdvo->sdvo_lvds_fixed_mode))
return false; return false;
(void) intel_sdvo_set_input_timings_for_mode(intel_sdvo, (void) intel_sdvo_set_input_timings_for_mode(intel_sdvo,
@ -1077,9 +1070,10 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
} }
/* Make the CRTC code factor in the SDVO pixel multiplier. The /* Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will be told of the multiplier during mode_set. * SDVO device will factor out the multiplier during mode_set.
*/ */
adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode); multiplier = intel_sdvo_get_pixel_multiplier(adjusted_mode);
intel_mode_set_pixel_multiplier(adjusted_mode, multiplier);
return true; return true;
} }
@ -1093,10 +1087,11 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
struct drm_crtc *crtc = encoder->crtc; struct drm_crtc *crtc = encoder->crtc;
struct intel_crtc *intel_crtc = to_intel_crtc(crtc); struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder); struct intel_sdvo *intel_sdvo = enc_to_intel_sdvo(encoder);
u32 sdvox = 0; u32 sdvox;
int sdvo_pixel_multiply, rate;
struct intel_sdvo_in_out_map in_out; struct intel_sdvo_in_out_map in_out;
struct intel_sdvo_dtd input_dtd; struct intel_sdvo_dtd input_dtd;
int pixel_multiplier = intel_mode_get_pixel_multiplier(adjusted_mode);
int rate;
if (!mode) if (!mode)
return; return;
@ -1114,28 +1109,23 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
SDVO_CMD_SET_IN_OUT_MAP, SDVO_CMD_SET_IN_OUT_MAP,
&in_out, sizeof(in_out)); &in_out, sizeof(in_out));
if (intel_sdvo->is_hdmi) { /* Set the output timings to the screen */
if (!intel_sdvo_set_avi_infoframe(intel_sdvo, mode)) if (!intel_sdvo_set_target_output(intel_sdvo,
return; intel_sdvo->attached_output))
return;
sdvox |= SDVO_AUDIO_ENABLE;
}
/* We have tried to get input timing in mode_fixup, and filled into /* We have tried to get input timing in mode_fixup, and filled into
adjusted_mode */ * 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;
/* If it's a TV, we already set the output timing in mode_fixup.
* Otherwise, the output timing is equal to the input timing.
*/ */
if (!intel_sdvo->is_tv && !intel_sdvo->is_lvds) { if (intel_sdvo->is_tv || intel_sdvo->is_lvds) {
input_dtd = intel_sdvo->input_dtd;
} else {
/* Set the output timing to the screen */ /* Set the output timing to the screen */
if (!intel_sdvo_set_target_output(intel_sdvo, if (!intel_sdvo_set_target_output(intel_sdvo,
intel_sdvo->attached_output)) intel_sdvo->attached_output))
return; return;
intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
(void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd); (void) intel_sdvo_set_output_timing(intel_sdvo, &input_dtd);
} }
@ -1143,31 +1133,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
if (!intel_sdvo_set_target_input(intel_sdvo)) if (!intel_sdvo_set_target_input(intel_sdvo))
return; return;
if (intel_sdvo->is_tv) { if (intel_sdvo->is_hdmi &&
if (!intel_sdvo_set_tv_format(intel_sdvo)) !intel_sdvo_set_avi_infoframe(intel_sdvo, mode))
return; return;
}
/* We would like to use intel_sdvo_create_preferred_input_timing() to if (intel_sdvo->is_tv &&
* provide the device with a timing it can support, if it supports that !intel_sdvo_set_tv_format(intel_sdvo))
* feature. However, presumably we would need to adjust the CRTC to return;
* output the preferred timing, and we don't support that currently.
*/
#if 0
success = intel_sdvo_create_preferred_input_timing(encoder, clock,
width, height);
if (success) {
struct intel_sdvo_dtd *input_dtd;
intel_sdvo_get_preferred_input_timing(encoder, &input_dtd);
intel_sdvo_set_input_timing(encoder, &input_dtd);
}
#else
(void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd); (void) intel_sdvo_set_input_timing(intel_sdvo, &input_dtd);
#endif
sdvo_pixel_multiply = intel_sdvo_get_pixel_multiplier(mode); switch (pixel_multiplier) {
switch (sdvo_pixel_multiply) { default:
case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break; case 1: rate = SDVO_CLOCK_RATE_MULT_1X; break;
case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break; case 2: rate = SDVO_CLOCK_RATE_MULT_2X; break;
case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break; case 4: rate = SDVO_CLOCK_RATE_MULT_4X; break;
@ -1177,13 +1154,13 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
/* Set the SDVO control regs. */ /* Set the SDVO control regs. */
if (IS_I965G(dev)) { if (IS_I965G(dev)) {
sdvox |= SDVO_BORDER_ENABLE; sdvox = SDVO_BORDER_ENABLE;
if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC) if (adjusted_mode->flags & DRM_MODE_FLAG_PVSYNC)
sdvox |= SDVO_VSYNC_ACTIVE_HIGH; sdvox |= SDVO_VSYNC_ACTIVE_HIGH;
if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC) if (adjusted_mode->flags & DRM_MODE_FLAG_PHSYNC)
sdvox |= SDVO_HSYNC_ACTIVE_HIGH; sdvox |= SDVO_HSYNC_ACTIVE_HIGH;
} else { } else {
sdvox |= I915_READ(intel_sdvo->sdvo_reg); sdvox = I915_READ(intel_sdvo->sdvo_reg);
switch (intel_sdvo->sdvo_reg) { switch (intel_sdvo->sdvo_reg) {
case SDVOB: case SDVOB:
sdvox &= SDVOB_PRESERVE_MASK; sdvox &= SDVOB_PRESERVE_MASK;
@ -1196,16 +1173,18 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
} }
if (intel_crtc->pipe == 1) if (intel_crtc->pipe == 1)
sdvox |= SDVO_PIPE_B_SELECT; sdvox |= SDVO_PIPE_B_SELECT;
if (intel_sdvo->is_hdmi)
sdvox |= SDVO_AUDIO_ENABLE;
if (IS_I965G(dev)) { if (IS_I965G(dev)) {
/* done in crtc_mode_set as the dpll_md reg must be written early */ /* done in crtc_mode_set as the dpll_md reg must be written early */
} else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) { } else if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev)) {
/* done in crtc_mode_set as it lives inside the dpll register */ /* done in crtc_mode_set as it lives inside the dpll register */
} else { } else {
sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT; sdvox |= (pixel_multiplier - 1) << SDVO_PORT_MULTIPLY_SHIFT;
} }
if (intel_sdvo->sdvo_flags & SDVO_NEED_TO_STALL) if (input_dtd.part2.sdvo_flags & SDVO_NEED_TO_STALL)
sdvox |= SDVO_STALL_SELECT; sdvox |= SDVO_STALL_SELECT;
intel_sdvo_write_sdvox(intel_sdvo, sdvox); intel_sdvo_write_sdvox(intel_sdvo, sdvox);
} }
@ -1692,6 +1671,10 @@ end:
if (newmode->type & DRM_MODE_TYPE_PREFERRED) { if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
intel_sdvo->sdvo_lvds_fixed_mode = intel_sdvo->sdvo_lvds_fixed_mode =
drm_mode_duplicate(connector->dev, newmode); drm_mode_duplicate(connector->dev, newmode);
drm_mode_set_crtcinfo(intel_sdvo->sdvo_lvds_fixed_mode,
0);
intel_sdvo->is_lvds = true; intel_sdvo->is_lvds = true;
break; break;
} }