drm/i915: enable sdvo lvds scaling function.

Currently we implemented basic sdvo lvds function,
But except for sdvo lvds fixed mode, we can not switch
to other modes, otherwise display get black. The patch
handle three operations to enable sdvo lvds. At first
duplicate sdvo fixed mode for adjustment, then according
to fixed mode line valid all modes, at last adjust input
mode to fit our requirement.

Acked by Li Peng <peng.li@linux.intel.com>
Signed-off-by: Ma Ling <ling.ma@intel.com>
Reviewed-by: Ian Romanick <idr@freedesktop.org>
Signed-off-by: Eric Anholt <eric@anholt.net>
This commit is contained in:
ling.ma@intel.com 2009-06-30 11:35:35 +08:00 committed by Eric Anholt
parent 6ff4fd0567
commit 12682a9711
2 changed files with 103 additions and 12 deletions

View File

@ -68,11 +68,22 @@ struct intel_sdvo_priv {
* This is set if we treat the device as HDMI, instead of DVI.
*/
bool is_hdmi;
/**
* This is set if we detect output of sdvo device as LVDS.
*/
bool is_lvds;
/**
* This is sdvo flags for input timing.
*/
uint8_t sdvo_flags;
/**
* This is sdvo fixed pannel mode pointer
*/
struct drm_display_mode *sdvo_lvds_fixed_mode;
/**
* Returned SDTV resolutions allowed for the current format, if the
* device reported it.
@ -592,6 +603,7 @@ intel_sdvo_create_preferred_input_timing(struct intel_output *output,
uint16_t height)
{
struct intel_sdvo_preferred_input_timing_args args;
struct intel_sdvo_priv *sdvo_priv = output->dev_priv;
uint8_t status;
memset(&args, 0, sizeof(args));
@ -599,7 +611,12 @@ intel_sdvo_create_preferred_input_timing(struct intel_output *output,
args.width = width;
args.height = height;
args.interlace = 0;
args.scaled = 0;
if (sdvo_priv->is_lvds &&
(sdvo_priv->sdvo_lvds_fixed_mode->hdisplay != width ||
sdvo_priv->sdvo_lvds_fixed_mode->vdisplay != height))
args.scaled = 1;
intel_sdvo_write_cmd(output, SDVO_CMD_CREATE_PREFERRED_INPUT_TIMING,
&args, sizeof(args));
status = intel_sdvo_read_response(output, NULL, 0);
@ -944,12 +961,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
struct intel_output *output = enc_to_intel_output(encoder);
struct intel_sdvo_priv *dev_priv = output->dev_priv;
if (!dev_priv->is_tv) {
/* Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will be told of the multiplier during mode_set.
*/
adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
} else {
if (dev_priv->is_tv) {
struct intel_sdvo_dtd output_dtd;
bool success;
@ -980,6 +992,7 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
intel_sdvo_get_preferred_input_timing(output,
&input_dtd);
intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags;
drm_mode_set_crtcinfo(adjusted_mode, 0);
@ -990,6 +1003,52 @@ static bool intel_sdvo_mode_fixup(struct drm_encoder *encoder,
} else {
return false;
}
} else if (dev_priv->is_lvds) {
struct intel_sdvo_dtd output_dtd;
bool success;
drm_mode_set_crtcinfo(dev_priv->sdvo_lvds_fixed_mode, 0);
/* Set output timings */
intel_sdvo_get_dtd_from_mode(&output_dtd,
dev_priv->sdvo_lvds_fixed_mode);
intel_sdvo_set_target_output(output,
dev_priv->controlled_output);
intel_sdvo_set_output_timing(output, &output_dtd);
/* Set the input timing to the screen. Assume always input 0. */
intel_sdvo_set_target_input(output, true, false);
success = intel_sdvo_create_preferred_input_timing(
output,
mode->clock / 10,
mode->hdisplay,
mode->vdisplay);
if (success) {
struct intel_sdvo_dtd input_dtd;
intel_sdvo_get_preferred_input_timing(output,
&input_dtd);
intel_sdvo_get_mode_from_dtd(adjusted_mode, &input_dtd);
dev_priv->sdvo_flags = input_dtd.part2.sdvo_flags;
drm_mode_set_crtcinfo(adjusted_mode, 0);
mode->clock = adjusted_mode->clock;
adjusted_mode->clock *=
intel_sdvo_get_pixel_multiplier(mode);
} else {
return false;
}
} else {
/* Make the CRTC code factor in the SDVO pixel multiplier. The
* SDVO device will be told of the multiplier during mode_set.
*/
adjusted_mode->clock *= intel_sdvo_get_pixel_multiplier(mode);
}
return true;
}
@ -1033,15 +1092,16 @@ 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 (sdvo_priv->is_tv)
if (sdvo_priv->is_tv || sdvo_priv->is_lvds) {
intel_sdvo_get_dtd_from_mode(&input_dtd, adjusted_mode);
else
input_dtd.part2.sdvo_flags = sdvo_priv->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.
*/
if (!sdvo_priv->is_tv) {
if (!sdvo_priv->is_tv && !sdvo_priv->is_lvds) {
/* Set the output timing to the screen */
intel_sdvo_set_target_output(output,
sdvo_priv->controlled_output);
@ -1116,6 +1176,8 @@ static void intel_sdvo_mode_set(struct drm_encoder *encoder,
sdvox |= (sdvo_pixel_multiply - 1) << SDVO_PORT_MULTIPLY_SHIFT;
}
if (sdvo_priv->sdvo_flags & SDVO_NEED_TO_STALL)
sdvox |= SDVO_STALL_SELECT;
intel_sdvo_write_sdvox(output, sdvox);
}
@ -1276,6 +1338,17 @@ static int intel_sdvo_mode_valid(struct drm_connector *connector,
if (sdvo_priv->pixel_clock_max < mode->clock)
return MODE_CLOCK_HIGH;
if (sdvo_priv->is_lvds == true) {
if (sdvo_priv->sdvo_lvds_fixed_mode == NULL)
return MODE_PANEL;
if (mode->hdisplay > sdvo_priv->sdvo_lvds_fixed_mode->hdisplay)
return MODE_PANEL;
if (mode->vdisplay > sdvo_priv->sdvo_lvds_fixed_mode->vdisplay)
return MODE_PANEL;
}
return MODE_OK;
}
@ -1549,6 +1622,8 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct drm_i915_private *dev_priv = connector->dev->dev_private;
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
struct drm_display_mode *newmode;
/*
* Attempt to get the mode list from DDC.
@ -1557,11 +1632,10 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
*/
intel_ddc_get_modes(intel_output);
if (list_empty(&connector->probed_modes) == false)
return;
goto end;
/* Fetch modes from VBT */
if (dev_priv->sdvo_lvds_vbt_mode != NULL) {
struct drm_display_mode *newmode;
newmode = drm_mode_duplicate(connector->dev,
dev_priv->sdvo_lvds_vbt_mode);
if (newmode != NULL) {
@ -1571,6 +1645,16 @@ static void intel_sdvo_get_lvds_modes(struct drm_connector *connector)
drm_mode_probed_add(connector, newmode);
}
}
end:
list_for_each_entry(newmode, &connector->probed_modes, head) {
if (newmode->type & DRM_MODE_TYPE_PREFERRED) {
sdvo_priv->sdvo_lvds_fixed_mode =
drm_mode_duplicate(connector->dev, newmode);
break;
}
}
}
static int intel_sdvo_get_modes(struct drm_connector *connector)
@ -1593,14 +1677,20 @@ static int intel_sdvo_get_modes(struct drm_connector *connector)
static void intel_sdvo_destroy(struct drm_connector *connector)
{
struct intel_output *intel_output = to_intel_output(connector);
struct intel_sdvo_priv *sdvo_priv = intel_output->dev_priv;
if (intel_output->i2c_bus)
intel_i2c_destroy(intel_output->i2c_bus);
if (intel_output->ddc_bus)
intel_i2c_destroy(intel_output->ddc_bus);
if (sdvo_priv->sdvo_lvds_fixed_mode != NULL)
drm_mode_destroy(connector->dev,
sdvo_priv->sdvo_lvds_fixed_mode);
drm_sysfs_connector_remove(connector);
drm_connector_cleanup(connector);
kfree(intel_output);
}

View File

@ -715,6 +715,7 @@ struct intel_sdvo_enhancements_arg {
#define SDVO_HBUF_TX_ONCE (2 << 6)
#define SDVO_HBUF_TX_VSYNC (3 << 6)
#define SDVO_CMD_GET_AUDIO_TX_INFO 0x9c
#define SDVO_NEED_TO_STALL (1 << 7)
struct intel_sdvo_encode{
u8 dvi_rev;