diff options
Diffstat (limited to 'drivers/gpu/drm/radeon/radeon_connectors.c')
| -rw-r--r-- | drivers/gpu/drm/radeon/radeon_connectors.c | 611 |
1 files changed, 455 insertions, 156 deletions
diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c index 8c9a8115b63..44831197e82 100644 --- a/drivers/gpu/drm/radeon/radeon_connectors.c +++ b/drivers/gpu/drm/radeon/radeon_connectors.c @@ -23,26 +23,15 @@ * Authors: Dave Airlie * Alex Deucher */ -#include "drmP.h" -#include "drm_edid.h" -#include "drm_crtc_helper.h" -#include "drm_fb_helper.h" -#include "radeon_drm.h" +#include <drm/drmP.h> +#include <drm/drm_edid.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_fb_helper.h> +#include <drm/radeon_drm.h> #include "radeon.h" #include "atom.h" -extern void -radeon_combios_connected_scratch_regs(struct drm_connector *connector, - struct drm_encoder *encoder, - bool connected); -extern void -radeon_atombios_connected_scratch_regs(struct drm_connector *connector, - struct drm_encoder *encoder, - bool connected); - -extern void -radeon_legacy_backlight_init(struct radeon_encoder *radeon_encoder, - struct drm_connector *drm_connector); +#include <linux/pm_runtime.h> void radeon_connector_hotplug(struct drm_connector *connector) { @@ -59,19 +48,39 @@ void radeon_connector_hotplug(struct drm_connector *connector) radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd); /* if the connector is already off, don't turn it back on */ + /* FIXME: This access isn't protected by any locks. */ if (connector->dpms != DRM_MODE_DPMS_ON) return; /* just deal with DP (not eDP) here. */ if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) { - int saved_dpms = connector->dpms; - - /* Only turn off the display it it's physically disconnected */ - if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); - else if (radeon_dp_needs_link_train(radeon_connector)) - drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); - connector->dpms = saved_dpms; + struct radeon_connector_atom_dig *dig_connector = + radeon_connector->con_priv; + + /* if existing sink type was not DP no need to retrain */ + if (dig_connector->dp_sink_type != CONNECTOR_OBJECT_ID_DISPLAYPORT) + return; + + /* first get sink type as it may be reset after (un)plug */ + dig_connector->dp_sink_type = radeon_dp_getsinktype(radeon_connector); + /* don't do anything if sink is not display port, i.e., + * passive dp->(dvi|hdmi) adaptor + */ + if (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) { + int saved_dpms = connector->dpms; + /* Only turn off the display if it's physically disconnected */ + if (!radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) { + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF); + } else if (radeon_dp_needs_link_train(radeon_connector)) { + /* set it to OFF so that drm_helper_connector_dpms() + * won't return immediately since the current state + * is ON at this point. + */ + connector->dpms = DRM_MODE_DPMS_OFF; + drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON); + } + connector->dpms = saved_dpms; + } } } @@ -81,9 +90,124 @@ static void radeon_property_change_mode(struct drm_encoder *encoder) if (crtc && crtc->enabled) { drm_crtc_helper_set_mode(crtc, &crtc->mode, - crtc->x, crtc->y, crtc->fb); + crtc->x, crtc->y, crtc->primary->fb); + } +} + +int radeon_get_monitor_bpc(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + struct radeon_connector_atom_dig *dig_connector; + int bpc = 8; + int mode_clock, max_tmds_clock; + + switch (connector->connector_type) { + case DRM_MODE_CONNECTOR_DVII: + case DRM_MODE_CONNECTOR_HDMIB: + if (radeon_connector->use_digital) { + if (drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + } + break; + case DRM_MODE_CONNECTOR_DVID: + case DRM_MODE_CONNECTOR_HDMIA: + if (drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + break; + case DRM_MODE_CONNECTOR_DisplayPort: + dig_connector = radeon_connector->con_priv; + if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || + (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) || + drm_detect_hdmi_monitor(radeon_connector->edid)) { + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + } + break; + case DRM_MODE_CONNECTOR_eDP: + case DRM_MODE_CONNECTOR_LVDS: + if (connector->display_info.bpc) + bpc = connector->display_info.bpc; + else if (ASIC_IS_DCE41(rdev) || ASIC_IS_DCE5(rdev)) { + struct drm_connector_helper_funcs *connector_funcs = + connector->helper_private; + struct drm_encoder *encoder = connector_funcs->best_encoder(connector); + struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); + struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv; + + if (dig->lcd_misc & ATOM_PANEL_MISC_V13_6BIT_PER_COLOR) + bpc = 6; + else if (dig->lcd_misc & ATOM_PANEL_MISC_V13_8BIT_PER_COLOR) + bpc = 8; + } + break; + } + + if (drm_detect_hdmi_monitor(radeon_connector->edid)) { + /* hdmi deep color only implemented on DCE4+ */ + if ((bpc > 8) && !ASIC_IS_DCE4(rdev)) { + DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 8 bpc.\n", + connector->name, bpc); + bpc = 8; + } + + /* + * Pre DCE-8 hw can't handle > 12 bpc, and more than 12 bpc doesn't make + * much sense without support for > 12 bpc framebuffers. RGB 4:4:4 at + * 12 bpc is always supported on hdmi deep color sinks, as this is + * required by the HDMI-1.3 spec. Clamp to a safe 12 bpc maximum. + */ + if (bpc > 12) { + DRM_DEBUG("%s: HDMI deep color %d bpc unsupported. Using 12 bpc.\n", + connector->name, bpc); + bpc = 12; + } + + /* Any defined maximum tmds clock limit we must not exceed? */ + if (connector->max_tmds_clock > 0) { + /* mode_clock is clock in kHz for mode to be modeset on this connector */ + mode_clock = radeon_connector->pixelclock_for_modeset; + + /* Maximum allowable input clock in kHz */ + max_tmds_clock = connector->max_tmds_clock * 1000; + + DRM_DEBUG("%s: hdmi mode dotclock %d kHz, max tmds input clock %d kHz.\n", + connector->name, mode_clock, max_tmds_clock); + + /* Check if bpc is within clock limit. Try to degrade gracefully otherwise */ + if ((bpc == 12) && (mode_clock * 3/2 > max_tmds_clock)) { + if ((connector->display_info.edid_hdmi_dc_modes & DRM_EDID_HDMI_DC_30) && + (mode_clock * 5/4 <= max_tmds_clock)) + bpc = 10; + else + bpc = 8; + + DRM_DEBUG("%s: HDMI deep color 12 bpc exceeds max tmds clock. Using %d bpc.\n", + connector->name, bpc); + } + + if ((bpc == 10) && (mode_clock * 5/4 > max_tmds_clock)) { + bpc = 8; + DRM_DEBUG("%s: HDMI deep color 10 bpc exceeds max tmds clock. Using %d bpc.\n", + connector->name, bpc); + } + } } + + if ((radeon_deep_color == 0) && (bpc > 8)) + bpc = 8; + + DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n", + connector->name, connector->display_info.bpc, bpc); + + return bpc; } + static void radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_connector_status status) { @@ -123,7 +247,7 @@ radeon_connector_update_scratch_regs(struct drm_connector *connector, enum drm_c } } -struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int encoder_type) +static struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int encoder_type) { struct drm_mode_object *obj; struct drm_encoder *encoder; @@ -144,7 +268,7 @@ struct drm_encoder *radeon_find_encoder(struct drm_connector *connector, int enc return NULL; } -struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) +static struct drm_encoder *radeon_best_single_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; struct drm_mode_object *obj; @@ -196,13 +320,17 @@ radeon_connector_analog_encoder_conflict_solve(struct drm_connector *connector, continue; if (priority == true) { - DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", drm_get_connector_name(conflict)); - DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(connector)); + DRM_DEBUG_KMS("1: conflicting encoders switching off %s\n", + conflict->name); + DRM_DEBUG_KMS("in favor of %s\n", + connector->name); conflict->status = connector_status_disconnected; radeon_connector_update_scratch_regs(conflict, connector_status_disconnected); } else { - DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", drm_get_connector_name(connector)); - DRM_DEBUG_KMS("in favor of %s\n", drm_get_connector_name(conflict)); + DRM_DEBUG_KMS("2: conflicting encoders switching off %s\n", + connector->name); + DRM_DEBUG_KMS("in favor of %s\n", + conflict->name); current_status = connector_status_disconnected; } break; @@ -295,7 +423,7 @@ static void radeon_add_common_modes(struct drm_encoder *encoder, struct drm_conn } } -int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, +static int radeon_connector_set_property(struct drm_connector *connector, struct drm_property *property, uint64_t val) { struct drm_device *dev = connector->dev; @@ -325,6 +453,36 @@ int radeon_connector_set_property(struct drm_connector *connector, struct drm_pr } } + if (property == rdev->mode_info.audio_property) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_connector->audio != val) { + radeon_connector->audio = val; + radeon_property_change_mode(&radeon_encoder->base); + } + } + + if (property == rdev->mode_info.dither_property) { + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + /* need to find digital encoder on connector */ + encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); + if (!encoder) + return 0; + + radeon_encoder = to_radeon_encoder(encoder); + + if (radeon_connector->dither != val) { + radeon_connector->dither = val; + radeon_property_change_mode(&radeon_encoder->base); + } + } + if (property == rdev->mode_info.underscan_property) { /* need to find digital encoder on connector */ encoder = radeon_find_encoder(connector, DRM_MODE_ENCODER_TMDS); @@ -540,6 +698,11 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct drm_encoder *encoder = radeon_best_single_encoder(connector); enum drm_connector_status ret = connector_status_disconnected; + int r; + + r = pm_runtime_get_sync(connector->dev->dev); + if (r < 0) + return connector_status_disconnected; if (encoder) { struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder); @@ -565,6 +728,8 @@ radeon_lvds_detect(struct drm_connector *connector, bool force) /* check acpi lid status ??? */ radeon_connector_update_scratch_regs(connector, ret); + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); return ret; } @@ -616,13 +781,13 @@ static int radeon_lvds_set_property(struct drm_connector *connector, } -struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { +static const struct drm_connector_helper_funcs radeon_lvds_connector_helper_funcs = { .get_modes = radeon_lvds_get_modes, .mode_valid = radeon_lvds_mode_valid, .best_encoder = radeon_best_single_encoder, }; -struct drm_connector_funcs radeon_lvds_connector_funcs = { +static const struct drm_connector_funcs radeon_lvds_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_lvds_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -664,13 +829,18 @@ radeon_vga_detect(struct drm_connector *connector, bool force) struct drm_encoder_helper_funcs *encoder_funcs; bool dret = false; enum drm_connector_status ret = connector_status_disconnected; + int r; + + r = pm_runtime_get_sync(connector->dev->dev); + if (r < 0) + return connector_status_disconnected; encoder = radeon_best_single_encoder(connector); if (!encoder) ret = connector_status_disconnected; if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector); + dret = radeon_ddc_probe(radeon_connector, false); if (dret) { radeon_connector->detected_by_load = false; if (radeon_connector->edid) { @@ -681,7 +851,7 @@ radeon_vga_detect(struct drm_connector *connector, bool force) if (!radeon_connector->edid) { DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", - drm_get_connector_name(connector)); + connector->name); ret = connector_status_connected; } else { radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); @@ -704,9 +874,8 @@ radeon_vga_detect(struct drm_connector *connector, bool force) * detected a monitor via load. */ if (radeon_connector->detected_by_load) - return connector->status; - else - return ret; + ret = connector->status; + goto out; } if (radeon_connector->dac_load_detect && encoder) { @@ -731,16 +900,21 @@ radeon_vga_detect(struct drm_connector *connector, bool force) } radeon_connector_update_scratch_regs(connector, ret); + +out: + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + return ret; } -struct drm_connector_helper_funcs radeon_vga_connector_helper_funcs = { +static const struct drm_connector_helper_funcs radeon_vga_connector_helper_funcs = { .get_modes = radeon_vga_get_modes, .mode_valid = radeon_vga_mode_valid, .best_encoder = radeon_best_single_encoder, }; -struct drm_connector_funcs radeon_vga_connector_funcs = { +static const struct drm_connector_funcs radeon_vga_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_vga_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -787,10 +961,15 @@ radeon_tv_detect(struct drm_connector *connector, bool force) struct drm_encoder_helper_funcs *encoder_funcs; struct radeon_connector *radeon_connector = to_radeon_connector(connector); enum drm_connector_status ret = connector_status_disconnected; + int r; if (!radeon_connector->dac_load_detect) return ret; + r = pm_runtime_get_sync(connector->dev->dev); + if (r < 0) + return connector_status_disconnected; + encoder = radeon_best_single_encoder(connector); if (!encoder) ret = connector_status_disconnected; @@ -801,16 +980,18 @@ radeon_tv_detect(struct drm_connector *connector, bool force) if (ret == connector_status_connected) ret = radeon_connector_analog_encoder_conflict_solve(connector, encoder, ret, false); radeon_connector_update_scratch_regs(connector, ret); + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); return ret; } -struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = { +static const struct drm_connector_helper_funcs radeon_tv_connector_helper_funcs = { .get_modes = radeon_tv_get_modes, .mode_valid = radeon_tv_mode_valid, .best_encoder = radeon_best_single_encoder, }; -struct drm_connector_funcs radeon_tv_connector_funcs = { +static const struct drm_connector_funcs radeon_tv_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_tv_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -827,6 +1008,27 @@ static int radeon_dvi_get_modes(struct drm_connector *connector) return ret; } +static bool radeon_check_hpd_status_unchanged(struct drm_connector *connector) +{ + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; + struct radeon_connector *radeon_connector = to_radeon_connector(connector); + enum drm_connector_status status; + + /* We only trust HPD on R600 and newer ASICS. */ + if (rdev->family >= CHIP_R600 + && radeon_connector->hpd.hpd != RADEON_HPD_NONE) { + if (radeon_hpd_sense(rdev, radeon_connector->hpd.hpd)) + status = connector_status_connected; + else + status = connector_status_disconnected; + if (connector->status == status) + return true; + } + + return false; +} + /* * DVI is complicated * Do a DDC probe, if DDC probe passes, get the full EDID so @@ -847,12 +1049,21 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) struct drm_encoder *encoder = NULL; struct drm_encoder_helper_funcs *encoder_funcs; struct drm_mode_object *obj; - int i; + int i, r; enum drm_connector_status ret = connector_status_disconnected; - bool dret = false; + bool dret = false, broken_edid = false; + + r = pm_runtime_get_sync(connector->dev->dev); + if (r < 0) + return connector_status_disconnected; + + if (!force && radeon_check_hpd_status_unchanged(connector)) { + ret = connector->status; + goto exit; + } if (radeon_connector->ddc_bus) - dret = radeon_ddc_probe(radeon_connector); + dret = radeon_ddc_probe(radeon_connector, false); if (dret) { radeon_connector->detected_by_load = false; if (radeon_connector->edid) { @@ -863,13 +1074,17 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) if (!radeon_connector->edid) { DRM_ERROR("%s: probed a monitor but no|invalid EDID\n", - drm_get_connector_name(connector)); + connector->name); /* rs690 seems to have a problem with connectors not existing and always * return a block of 0's. If we see this just stop polling on this output */ if ((rdev->family == CHIP_RS690 || rdev->family == CHIP_RS740) && radeon_connector->base.null_edid_counter) { ret = connector_status_disconnected; - DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", drm_get_connector_name(connector)); + DRM_ERROR("%s: detected RS690 floating bus bug, stopping ddc detect\n", + connector->name); radeon_connector->ddc_bus = NULL; + } else { + ret = connector_status_connected; + broken_edid = true; /* defer use_digital to later */ } } else { radeon_connector->use_digital = !!(radeon_connector->edid->input & DRM_EDID_INPUT_DIGITAL); @@ -946,15 +1161,30 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) encoder = obj_to_encoder(obj); + if (encoder->encoder_type != DRM_MODE_ENCODER_DAC && + encoder->encoder_type != DRM_MODE_ENCODER_TVDAC) + continue; + encoder_funcs = encoder->helper_private; if (encoder_funcs->detect) { - if (ret != connector_status_connected) { - ret = encoder_funcs->detect(encoder, connector); - if (ret == connector_status_connected) { - radeon_connector->use_digital = false; + if (!broken_edid) { + if (ret != connector_status_connected) { + /* deal with analog monitors without DDC */ + ret = encoder_funcs->detect(encoder, connector); + if (ret == connector_status_connected) { + radeon_connector->use_digital = false; + } + if (ret != connector_status_disconnected) + radeon_connector->detected_by_load = true; } - if (ret != connector_status_disconnected) - radeon_connector->detected_by_load = true; + } else { + enum drm_connector_status lret; + /* assume digital unless load detected otherwise */ + radeon_connector->use_digital = true; + lret = encoder_funcs->detect(encoder, connector); + DRM_DEBUG_KMS("load_detect %x returned: %x\n",encoder->encoder_type,lret); + if (lret == connector_status_connected) + radeon_connector->use_digital = false; } break; } @@ -972,6 +1202,7 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) * cases the DVI port is actually a virtual KVM port connected to the service * processor. */ +out: if ((!rdev->is_atom_bios) && (ret == connector_status_disconnected) && rdev->mode_info.bios_hardcoded_edid_size) { @@ -979,14 +1210,18 @@ radeon_dvi_detect(struct drm_connector *connector, bool force) ret = connector_status_connected; } -out: /* updated in get modes as well since we need to know if it's analog or digital */ radeon_connector_update_scratch_regs(connector, ret); + +exit: + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + return ret; } /* okay need to be smart in here about which encoder to pick */ -struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) +static struct drm_encoder *radeon_dvi_encoder(struct drm_connector *connector) { int enc_id = connector->encoder_ids[0]; struct radeon_connector *radeon_connector = to_radeon_connector(connector); @@ -1056,17 +1291,15 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector, (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) || (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_B)) return MODE_OK; - else if (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_HDMI_TYPE_A) { - if (0) { - /* HDMI 1.3+ supports max clock of 340 Mhz */ - if (mode->clock > 340000) - return MODE_CLOCK_HIGH; - else - return MODE_OK; - } else + else if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + /* HDMI 1.3+ supports max clock of 340 Mhz */ + if (mode->clock > 340000) return MODE_CLOCK_HIGH; - } else + else + return MODE_OK; + } else { return MODE_CLOCK_HIGH; + } } /* check against the max pixel clock */ @@ -1076,13 +1309,13 @@ static int radeon_dvi_mode_valid(struct drm_connector *connector, return MODE_OK; } -struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { +static const struct drm_connector_helper_funcs radeon_dvi_connector_helper_funcs = { .get_modes = radeon_dvi_get_modes, .mode_valid = radeon_dvi_mode_valid, .best_encoder = radeon_dvi_encoder, }; -struct drm_connector_funcs radeon_dvi_connector_funcs = { +static const struct drm_connector_funcs radeon_dvi_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_dvi_detect, .fill_modes = drm_helper_probe_single_connector_modes, @@ -1091,21 +1324,6 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = { .force = radeon_dvi_force, }; -static void radeon_dp_connector_destroy(struct drm_connector *connector) -{ - struct radeon_connector *radeon_connector = to_radeon_connector(connector); - struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; - - if (radeon_connector->edid) - kfree(radeon_connector->edid); - if (radeon_dig_connector->dp_i2c_bus) - radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus); - kfree(radeon_connector->con_priv); - drm_sysfs_connector_remove(connector); - drm_connector_cleanup(connector); - kfree(connector); -} - static int radeon_dp_get_modes(struct drm_connector *connector) { struct radeon_connector *radeon_connector = to_radeon_connector(connector); @@ -1232,7 +1450,7 @@ bool radeon_connector_is_dp12_capable(struct drm_connector *connector) struct radeon_device *rdev = dev->dev_private; if (ASIC_IS_DCE5(rdev) && - (rdev->clock.dp_extclk >= 53900) && + (rdev->clock.default_dispclk >= 53900) && radeon_connector_encoder_is_hbr2(connector)) { return true; } @@ -1249,6 +1467,16 @@ radeon_dp_detect(struct drm_connector *connector, bool force) enum drm_connector_status ret = connector_status_disconnected; struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; struct drm_encoder *encoder = radeon_best_single_encoder(connector); + int r; + + r = pm_runtime_get_sync(connector->dev->dev); + if (r < 0) + return connector_status_disconnected; + + if (!force && radeon_check_hpd_status_unchanged(connector)) { + ret = connector->status; + goto out; + } if (radeon_connector->edid) { kfree(radeon_connector->edid); @@ -1285,7 +1513,8 @@ radeon_dp_detect(struct drm_connector *connector, bool force) if (encoder) { /* setup ddc on the bridge */ radeon_atom_ext_encoder_setup_ddc(encoder); - if (radeon_ddc_probe(radeon_connector)) /* try DDC */ + /* bridge chips are always aux */ + if (radeon_ddc_probe(radeon_connector, true)) /* try DDC */ ret = connector_status_connected; else if (radeon_connector->dac_load_detect) { /* try load detection */ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private; @@ -1303,19 +1532,26 @@ radeon_dp_detect(struct drm_connector *connector, bool force) if (radeon_dp_getdpcd(radeon_connector)) ret = connector_status_connected; } else { - if (radeon_ddc_probe(radeon_connector)) + /* try non-aux ddc (DP to DVI/HDMI/etc. adapter) */ + if (radeon_ddc_probe(radeon_connector, false)) ret = connector_status_connected; } } } radeon_connector_update_scratch_regs(connector, ret); +out: + pm_runtime_mark_last_busy(connector->dev->dev); + pm_runtime_put_autosuspend(connector->dev->dev); + return ret; } static int radeon_dp_mode_valid(struct drm_connector *connector, struct drm_display_mode *mode) { + struct drm_device *dev = connector->dev; + struct radeon_device *rdev = dev->dev_private; struct radeon_connector *radeon_connector = to_radeon_connector(connector); struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv; @@ -1346,28 +1582,55 @@ static int radeon_dp_mode_valid(struct drm_connector *connector, return MODE_PANEL; } } - return MODE_OK; } else { if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) || - (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) + (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP)) { return radeon_dp_mode_valid_helper(connector, mode); - else - return MODE_OK; + } else { + if (ASIC_IS_DCE6(rdev) && drm_detect_hdmi_monitor(radeon_connector->edid)) { + /* HDMI 1.3+ supports max clock of 340 Mhz */ + if (mode->clock > 340000) + return MODE_CLOCK_HIGH; + } else { + if (mode->clock > 165000) + return MODE_CLOCK_HIGH; + } + } } + + return MODE_OK; } -struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = { +static const struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = { .get_modes = radeon_dp_get_modes, .mode_valid = radeon_dp_mode_valid, .best_encoder = radeon_dvi_encoder, }; -struct drm_connector_funcs radeon_dp_connector_funcs = { +static const struct drm_connector_funcs radeon_dp_connector_funcs = { .dpms = drm_helper_connector_dpms, .detect = radeon_dp_detect, .fill_modes = drm_helper_probe_single_connector_modes, .set_property = radeon_connector_set_property, - .destroy = radeon_dp_connector_destroy, + .destroy = radeon_connector_destroy, + .force = radeon_dvi_force, +}; + +static const struct drm_connector_funcs radeon_edp_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_lvds_set_property, + .destroy = radeon_connector_destroy, + .force = radeon_dvi_force, +}; + +static const struct drm_connector_funcs radeon_lvds_bridge_connector_funcs = { + .dpms = drm_helper_connector_dpms, + .detect = radeon_dp_detect, + .fill_modes = drm_helper_probe_single_connector_modes, + .set_property = radeon_lvds_set_property, + .destroy = radeon_connector_destroy, .force = radeon_dvi_force, }; @@ -1391,6 +1654,7 @@ radeon_add_atom_connector(struct drm_device *dev, uint32_t subpixel_order = SubPixelNone; bool shared_ddc = false; bool is_dp_bridge = false; + bool has_aux = false; if (connector_type == DRM_MODE_CONNECTOR_Unknown) return; @@ -1462,28 +1726,25 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; - drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); - drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { - /* add DP i2c bus */ - if (connector_type == DRM_MODE_CONNECTOR_eDP) - radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch"); - else - radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch"); - if (!radeon_dig_connector->dp_i2c_bus) - DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); - if (!radeon_connector->ddc_bus) + if (radeon_connector->ddc_bus) + has_aux = true; + else DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } switch (connector_type) { case DRM_MODE_CONNECTOR_VGA: case DRM_MODE_CONNECTOR_DVIA: default: + drm_connector_init(dev, &radeon_connector->base, + &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); connector->interlace_allowed = true; connector->doublescan_allowed = true; radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); break; @@ -1492,15 +1753,29 @@ radeon_add_atom_connector(struct drm_device *dev, case DRM_MODE_CONNECTOR_HDMIA: case DRM_MODE_CONNECTOR_HDMIB: case DRM_MODE_CONNECTOR_DisplayPort: - drm_connector_attach_property(&radeon_connector->base, + drm_connector_init(dev, &radeon_connector->base, + &radeon_dp_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_hborder_property, 0); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_vborder_property, 0); + + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.dither_property, + RADEON_FMT_DITHER_DISABLE); + + if (radeon_audio != 0) + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.audio_property, + RADEON_AUDIO_AUTO); + subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = true; if (connector_type == DRM_MODE_CONNECTOR_HDMIB) @@ -1509,14 +1784,18 @@ radeon_add_atom_connector(struct drm_device *dev, connector->doublescan_allowed = false; if (connector_type == DRM_MODE_CONNECTOR_DVII) { radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); } break; case DRM_MODE_CONNECTOR_LVDS: case DRM_MODE_CONNECTOR_eDP: - drm_connector_attach_property(&radeon_connector->base, + drm_connector_init(dev, &radeon_connector->base, + &radeon_lvds_bridge_connector_funcs, connector_type); + drm_connector_helper_add(&radeon_connector->base, + &radeon_dp_connector_helper_funcs); + drm_object_attach_property(&radeon_connector->base.base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); subpixel_order = SubPixelHorizontalRGB; @@ -1535,7 +1814,7 @@ radeon_add_atom_connector(struct drm_device *dev, DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); /* no HPD on analog connectors */ @@ -1553,7 +1832,7 @@ radeon_add_atom_connector(struct drm_device *dev, DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); /* no HPD on analog connectors */ @@ -1576,23 +1855,33 @@ radeon_add_atom_connector(struct drm_device *dev, DRM_ERROR("DVI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } subpixel_order = SubPixelHorizontalRGB; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.coherent_mode_property, 1); if (ASIC_IS_AVIVO(rdev)) { - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_hborder_property, 0); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_vborder_property, 0); } + if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.audio_property, + RADEON_AUDIO_AUTO); + } + if (ASIC_IS_AVIVO(rdev)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.dither_property, + RADEON_FMT_DITHER_DISABLE); + } if (connector_type == DRM_MODE_CONNECTOR_DVII) { radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); } @@ -1616,20 +1905,30 @@ radeon_add_atom_connector(struct drm_device *dev, if (!radeon_connector->ddc_bus) DRM_ERROR("HDMI: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.coherent_mode_property, 1); if (ASIC_IS_AVIVO(rdev)) { - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_hborder_property, 0); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_vborder_property, 0); } + if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.audio_property, + RADEON_AUDIO_AUTO); + } + if (ASIC_IS_AVIVO(rdev)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.dither_property, + RADEON_FMT_DITHER_DISABLE); + } subpixel_order = SubPixelHorizontalRGB; connector->interlace_allowed = true; if (connector_type == DRM_MODE_CONNECTOR_HDMIB) @@ -1646,29 +1945,38 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { - /* add DP i2c bus */ - radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch"); - if (!radeon_dig_connector->dp_i2c_bus) - DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); - if (!radeon_connector->ddc_bus) + if (radeon_connector->ddc_bus) + has_aux = true; + else DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } subpixel_order = SubPixelHorizontalRGB; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.coherent_mode_property, 1); if (ASIC_IS_AVIVO(rdev)) { - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_property, UNDERSCAN_OFF); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_hborder_property, 0); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.underscan_vborder_property, 0); } + if (ASIC_IS_DCE2(rdev) && (radeon_audio != 0)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.audio_property, + RADEON_AUDIO_AUTO); + } + if (ASIC_IS_AVIVO(rdev)) { + drm_object_attach_property(&radeon_connector->base.base, + rdev->mode_info.dither_property, + RADEON_FMT_DITHER_DISABLE); + + } connector->interlace_allowed = true; /* in theory with a DP to VGA converter... */ connector->doublescan_allowed = false; @@ -1679,18 +1987,16 @@ radeon_add_atom_connector(struct drm_device *dev, goto failed; radeon_dig_connector->igp_lane_info = igp_lane_info; radeon_connector->con_priv = radeon_dig_connector; - drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type); + drm_connector_init(dev, &radeon_connector->base, &radeon_edp_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs); if (i2c_bus->valid) { - /* add DP i2c bus */ - radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch"); - if (!radeon_dig_connector->dp_i2c_bus) - DRM_ERROR("DP: Failed to assign dp ddc bus! Check dmesg for i2c errors.\n"); radeon_connector->ddc_bus = radeon_i2c_lookup(rdev, i2c_bus); - if (!radeon_connector->ddc_bus) + if (radeon_connector->ddc_bus) + has_aux = true; + else DRM_ERROR("DP: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); subpixel_order = SubPixelHorizontalRGB; @@ -1703,10 +2009,10 @@ radeon_add_atom_connector(struct drm_device *dev, drm_connector_init(dev, &radeon_connector->base, &radeon_tv_connector_funcs, connector_type); drm_connector_helper_add(&radeon_connector->base, &radeon_tv_connector_helper_funcs); radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.tv_std_property, radeon_atombios_get_tv_info(rdev)); /* no HPD on analog connectors */ @@ -1727,7 +2033,7 @@ radeon_add_atom_connector(struct drm_device *dev, if (!radeon_connector->ddc_bus) DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); subpixel_order = SubPixelHorizontalRGB; @@ -1745,6 +2051,10 @@ radeon_add_atom_connector(struct drm_device *dev, connector->display_info.subpixel_order = subpixel_order; drm_sysfs_connector_add(connector); + + if (has_aux) + radeon_dp_aux_init(radeon_connector); + return; failed: @@ -1806,7 +2116,7 @@ radeon_add_legacy_connector(struct drm_device *dev, DRM_ERROR("VGA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); /* no HPD on analog connectors */ @@ -1824,7 +2134,7 @@ radeon_add_legacy_connector(struct drm_device *dev, DRM_ERROR("DVIA: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); /* no HPD on analog connectors */ @@ -1843,7 +2153,7 @@ radeon_add_legacy_connector(struct drm_device *dev, } if (connector_type == DRM_MODE_CONNECTOR_DVII) { radeon_connector->dac_load_detect = true; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, 1); } @@ -1867,10 +2177,10 @@ radeon_add_legacy_connector(struct drm_device *dev, */ if (rdev->family == CHIP_RS400 || rdev->family == CHIP_RS480) radeon_connector->dac_load_detect = false; - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.load_detect_property, radeon_connector->dac_load_detect); - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, rdev->mode_info.tv_std_property, radeon_combios_get_tv_info(rdev)); /* no HPD on analog connectors */ @@ -1886,7 +2196,7 @@ radeon_add_legacy_connector(struct drm_device *dev, if (!radeon_connector->ddc_bus) DRM_ERROR("LVDS: Failed to assign ddc bus! Check dmesg for i2c errors.\n"); } - drm_connector_attach_property(&radeon_connector->base, + drm_object_attach_property(&radeon_connector->base.base, dev->mode_config.scaling_mode_property, DRM_MODE_SCALE_FULLSCREEN); subpixel_order = SubPixelHorizontalRGB; @@ -1902,15 +2212,4 @@ radeon_add_legacy_connector(struct drm_device *dev, connector->polled = DRM_CONNECTOR_POLL_HPD; connector->display_info.subpixel_order = subpixel_order; drm_sysfs_connector_add(connector); - if (connector_type == DRM_MODE_CONNECTOR_LVDS) { - struct drm_encoder *drm_encoder; - - list_for_each_entry(drm_encoder, &dev->mode_config.encoder_list, head) { - struct radeon_encoder *radeon_encoder; - - radeon_encoder = to_radeon_encoder(drm_encoder); - if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_LVDS) - radeon_legacy_backlight_init(radeon_encoder, connector); - } - } } |
