2063 lines · 66.1 KB · C
RawDownload
/* * Copyright 2007-8 Advanced Micro Devices, Inc. * Copyright 2008 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. * * Authors: Dave Airlie *          Alex Deucher */ #include <drm/display/drm_dp_helper.h>#include <drm/drm_crtc_helper.h>#include <drm/drm_edid.h>#include <drm/drm_modeset_helper_vtables.h>#include <drm/drm_probe_helper.h>#include <drm/amdgpu_drm.h>#include "amdgpu.h"#include "atom.h"#include "atombios_encoders.h"#include "atombios_dp.h"#include "amdgpu_connectors.h"#include "amdgpu_i2c.h"#include "amdgpu_display.h" #include <linux/pm_runtime.h> void amdgpu_connector_hotplug(struct drm_connector *connector){	struct drm_device *dev = connector->dev;	struct amdgpu_device *adev = drm_to_adev(dev);	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); 	/* bail if the connector does not have hpd pin, e.g.,	 * VGA, TV, etc.	 */	if (amdgpu_connector->hpd.hpd == AMDGPU_HPD_NONE)		return; 	amdgpu_display_hpd_set_polarity(adev, amdgpu_connector->hpd.hpd); 	/* if the connector is already off, don't turn it back on */	if (connector->dpms != DRM_MODE_DPMS_ON)		return; 	/* just deal with DP (not eDP) here. */	if (connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) {		struct amdgpu_connector_atom_dig *dig_connector =			amdgpu_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 = amdgpu_atombios_dp_get_sinktype(amdgpu_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 &&		    amdgpu_display_hpd_sense(adev, amdgpu_connector->hpd.hpd) &&		    amdgpu_atombios_dp_needs_link_train(amdgpu_connector)) {			/* Don't start link training before we have the DPCD */			if (amdgpu_atombios_dp_get_dpcd(amdgpu_connector))				return; 			/* Turn the connector off and back on immediately, which			 * will trigger link training			 */			drm_helper_connector_dpms(connector, DRM_MODE_DPMS_OFF);			drm_helper_connector_dpms(connector, DRM_MODE_DPMS_ON);		}	}} static void amdgpu_connector_property_change_mode(struct drm_encoder *encoder){	struct drm_crtc *crtc = encoder->crtc; 	if (crtc && crtc->enabled) {		drm_crtc_helper_set_mode(crtc, &crtc->mode,					 crtc->x, crtc->y, crtc->primary->fb);	}} int amdgpu_connector_get_monitor_bpc(struct drm_connector *connector){	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);	struct amdgpu_connector_atom_dig *dig_connector;	int bpc = 8;	unsigned int mode_clock, max_tmds_clock; 	switch (connector->connector_type) {	case DRM_MODE_CONNECTOR_DVII:	case DRM_MODE_CONNECTOR_HDMIB:		if (amdgpu_connector->use_digital) {			if (connector->display_info.is_hdmi) {				if (connector->display_info.bpc)					bpc = connector->display_info.bpc;			}		}		break;	case DRM_MODE_CONNECTOR_DVID:	case DRM_MODE_CONNECTOR_HDMIA:		if (connector->display_info.is_hdmi) {			if (connector->display_info.bpc)				bpc = connector->display_info.bpc;		}		break;	case DRM_MODE_CONNECTOR_DisplayPort:		dig_connector = amdgpu_connector->con_priv;		if ((dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||		    (dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) ||		    connector->display_info.is_hdmi) {			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 {			const struct drm_connector_helper_funcs *connector_funcs =				connector->helper_private;			struct drm_encoder *encoder = connector_funcs->best_encoder(connector);			struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);			struct amdgpu_encoder_atom_dig *dig = amdgpu_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 (connector->display_info.is_hdmi) {		/*		 * 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->display_info.max_tmds_clock > 0) {			/* mode_clock is clock in kHz for mode to be modeset on this connector */			mode_clock = amdgpu_connector->pixelclock_for_modeset; 			/* Maximum allowable input clock in kHz */			max_tmds_clock = connector->display_info.max_tmds_clock; 			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_rgb444_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);			}		} else if (bpc > 8) {			/* max_tmds_clock missing, but hdmi spec mandates it for deep color. */			DRM_DEBUG("%s: Required max tmds clock for HDMI deep color missing. Using 8 bpc.\n",				  connector->name);			bpc = 8;		}	} 	if ((amdgpu_deep_color == 0) && (bpc > 8)) {		DRM_DEBUG("%s: Deep color disabled. Set amdgpu module param deep_color=1 to enable.\n",			  connector->name);		bpc = 8;	} 	DRM_DEBUG("%s: Display bpc=%d, returned bpc=%d\n",		  connector->name, connector->display_info.bpc, bpc); 	return bpc;} static voidamdgpu_connector_update_scratch_regs(struct drm_connector *connector,				      enum drm_connector_status status){	struct drm_encoder *best_encoder;	struct drm_encoder *encoder;	const struct drm_connector_helper_funcs *connector_funcs = connector->helper_private;	bool connected; 	best_encoder = connector_funcs->best_encoder(connector); 	drm_connector_for_each_possible_encoder(connector, encoder) {		if ((encoder == best_encoder) && (status == connector_status_connected))			connected = true;		else			connected = false; 		amdgpu_atombios_encoder_set_bios_scratch_regs(connector, encoder, connected);	}} static struct drm_encoder *amdgpu_connector_find_encoder(struct drm_connector *connector,			       int encoder_type){	struct drm_encoder *encoder; 	drm_connector_for_each_possible_encoder(connector, encoder) {		if (encoder->encoder_type == encoder_type)			return encoder;	} 	return NULL;} static const struct drm_edid *amdgpu_connector_get_hardcoded_edid(struct amdgpu_device *adev){	return drm_edid_dup(adev->mode_info.bios_hardcoded_edid);} static void amdgpu_connector_get_edid(struct drm_connector *connector){	struct drm_device *dev = connector->dev;	struct amdgpu_device *adev = drm_to_adev(dev);	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector); 	if (amdgpu_connector->edid)		return; 	/* on hw with routers, select right port */	if (amdgpu_connector->router.ddc_valid)		amdgpu_i2c_router_select_ddc_port(amdgpu_connector); 	if ((amdgpu_connector_encoder_get_dp_bridge_encoder_id(connector) !=	     ENCODER_OBJECT_ID_NONE) &&	    amdgpu_connector->ddc_bus->has_aux) {		amdgpu_connector->edid = drm_edid_read_ddc(connector,							  &amdgpu_connector->ddc_bus->aux.ddc);	} else if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||		   (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {		struct amdgpu_connector_atom_dig *dig = amdgpu_connector->con_priv; 		if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||		     dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) &&		    amdgpu_connector->ddc_bus->has_aux)			amdgpu_connector->edid = drm_edid_read_ddc(connector,								  &amdgpu_connector->ddc_bus->aux.ddc);		else if (amdgpu_connector->ddc_bus)			amdgpu_connector->edid = drm_edid_read_ddc(connector,								  &amdgpu_connector->ddc_bus->adapter);	} else if (amdgpu_connector->ddc_bus) {		amdgpu_connector->edid = drm_edid_read_ddc(connector,							  &amdgpu_connector->ddc_bus->adapter);	} 	if (!amdgpu_connector->edid) {		/* some laptops provide a hardcoded edid in rom for LCDs */		if (((connector->connector_type == DRM_MODE_CONNECTOR_LVDS) ||		     (connector->connector_type == DRM_MODE_CONNECTOR_eDP))) {			amdgpu_connector->edid = amdgpu_connector_get_hardcoded_edid(adev);			drm_edid_connector_update(connector, amdgpu_connector->edid);		}	}} static int amdgpu_connector_ddc_get_modes(struct drm_connector *connector){	struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);	int ret; 	if (amdgpu_connector->edid) {		drm_edid_connector_update(connector, amdgpu_connector->edid);		ret = drm_edid_connector_add_modes(connector);		return ret;	}	drm_edid_connector_update(connector, NULL);	return 0;} static struct drm_encoder *amdgpu_connector_best_single_encoder(struct drm_connector *connector){	struct drm_encoder *encoder; 	/* pick the first one */	drm_connector_for_each_possible_encoder(connector, encoder)		return encoder; 	return NULL;} static void amdgpu_get_native_mode(struct drm_connector *connector){	struct drm_encoder *encoder = amdgpu_connector_best_single_encoder(connector);	struct amdgpu_encoder *amdgpu_encoder; 	if (encoder == NULL)		return; 	amdgpu_encoder = to_amdgpu_encoder(encoder); 	if (!list_empty(&connector->probed_modes)) {		struct drm_display_mode *preferred_mode =			list_first_entry(&connector->probed_modes,					 struct drm_display_mode, head); 		amdgpu_encoder->native_mode = *preferred_mode;	} else {		amdgpu_encoder->native_mode.clock = 0;	}} static struct drm_display_mode *amdgpu_connector_lcd_native_mode(struct drm_encoder *encoder){	struct drm_device *dev = encoder->dev;	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);	struct drm_display_mode *mode = NULL;	struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode; 	if (native_mode->hdisplay != 0 &&	    native_mode->vdisplay != 0 &&	    native_mode->clock != 0) {		mode = drm_mode_duplicate(dev, native_mode);		if (!mode)			return NULL; 		mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;		drm_mode_set_name(mode); 		DRM_DEBUG_KMS("Adding native panel mode %s\n", mode->name);	} else if (native_mode->hdisplay != 0 &&		   native_mode->vdisplay != 0) {		/* mac laptops without an edid */		/* Note that this is not necessarily the exact panel mode,		 * but an approximation based on the cvt formula.  For these		 * systems we should ideally read the mode info out of the		 * registers or add a mode table, but this works and is much		 * simpler.		 */		mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false);		if (!mode)			return NULL; 		mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;		DRM_DEBUG_KMS("Adding cvt approximation of native panel mode %s\n", mode->name);	}	return mode;} static void amdgpu_connector_add_common_modes(struct drm_encoder *encoder,					       struct drm_connector *connector){	struct drm_device *dev = encoder->dev;	struct amdgpu_encoder *amdgpu_encoder = to_amdgpu_encoder(encoder);	struct drm_display_mode *mode = NULL;	struct drm_display_mode *native_mode = &amdgpu_encoder->native_mode;	int i;	int n;	struct mode_size {		char name[DRM_DISPLAY_MODE_LEN];		int w;		int h;	} common_modes[] = {		{  "640x480",  640,  480},		{  "800x600",  800,  600},		{ "1024x768", 1024,  768},		{ "1280x720", 1280,  720},		{ "1280x800", 1280,  800},		{"1280x1024", 1280, 1024},		{ "1440x900", 1440,  900},		{"1680x1050", 1680, 1050},		{"1600x1200", 1600, 1200},		{"1920x1080", 1920, 1080},		{"1920x1200", 1920, 1200}	}; 	n = ARRAY_SIZE(common_modes); 	for (i = 0; i < n; i++) {		if (amdgpu_encoder->devices & (ATOM_DEVICE_TV_SUPPORT)) {			if (common_modes[i].w > 1024 ||			    common_modes[i].h > 768)				continue;		}		if (amdgpu_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {			if (common_modes[i].w > native_mode->hdisplay ||			    common_modes[i].h > native_mode->vdisplay ||			    (common_modes[i].w == native_mode->hdisplay &&			     common_modes[i].h == native_mode->vdisplay))				continue;		} 		mode = drm_cvt_mode(dev, common_modes[i].w, common_modes[i].h, 60, false, false, false);		if (!mode)			return;		strscpy(mode->name, common_modes[i].name, DRM_DISPLAY_MODE_LEN); 		drm_mode_probed_add(connector, mode);	}} static int amdgpu_connector_set_property(struct drm_connector *connector,					  struct drm_property *property,					  uint64_t val){	struct drm_device *dev = connector->dev;	struct amdgpu_device *adev = drm_to_adev(dev);	struct drm_encoder *encoder;	struct amdgpu_encoder *amdgpu_encoder; 	if (property == adev->mode_info.coherent_mode_property) {		struct amdgpu_encoder_atom_dig *dig;		bool new_coherent_mode; 		/* need to find digital encoder on connector */		encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS);		if (!encoder)			return 0; 		amdgpu_encoder = to_amdgpu_encoder(encoder); 		if (!amdgpu_encoder->enc_priv)			return 0; 		dig = amdgpu_encoder->enc_priv;		new_coherent_mode = val ? true : false;		if (dig->coherent_mode != new_coherent_mode) {			dig->coherent_mode = new_coherent_mode;			amdgpu_connector_property_change_mode(&amdgpu_encoder->base);		}	} 	if (property == adev->mode_info.audio_property) {		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);		/* need to find digital encoder on connector */		encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS);		if (!encoder)			return 0; 		amdgpu_encoder = to_amdgpu_encoder(encoder); 		if (amdgpu_connector->audio != val) {			amdgpu_connector->audio = val;			amdgpu_connector_property_change_mode(&amdgpu_encoder->base);		}	} 	if (property == adev->mode_info.dither_property) {		struct amdgpu_connector *amdgpu_connector = to_amdgpu_connector(connector);		/* need to find digital encoder on connector */		encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS);		if (!encoder)			return 0; 		amdgpu_encoder = to_amdgpu_encoder(encoder); 		if (amdgpu_connector->dither != val) {			amdgpu_connector->dither = val;			amdgpu_connector_property_change_mode(&amdgpu_encoder->base);		}	} 	if (property == adev->mode_info.underscan_property) {		/* need to find digital encoder on connector */		encoder = amdgpu_connector_find_encoder(connector, DRM_MODE_ENCODER_TMDS);

Lines 1–500 of 2063.