diff options
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_display.c')
| -rw-r--r-- | drivers/gpu/drm/msm/dp/dp_display.c | 272 | 
1 files changed, 167 insertions, 105 deletions
| diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c index e175aa3fd3a9..6e971d552911 100644 --- a/drivers/gpu/drm/msm/dp/dp_display.c +++ b/drivers/gpu/drm/msm/dp/dp_display.c @@ -45,7 +45,7 @@ enum {  	ST_CONNECT_PENDING,  	ST_CONNECTED,  	ST_DISCONNECT_PENDING, -	ST_SUSPEND_PENDING, +	ST_DISPLAY_OFF,  	ST_SUSPENDED,  }; @@ -102,20 +102,20 @@ struct dp_display_private {  	struct dp_display_mode dp_mode;  	struct msm_dp dp_display; +	bool encoder_mode_set; +  	/* wait for audio signaling */  	struct completion audio_comp;  	/* event related only access by event thread */  	struct mutex event_mutex;  	wait_queue_head_t event_q; -	atomic_t hpd_state; +	u32 hpd_state;  	u32 event_pndx;  	u32 event_gndx;  	struct dp_event event_list[DP_EVENT_Q_MAX];  	spinlock_t event_lock; -	struct completion resume_comp; -  	struct dp_audio *audio;  }; @@ -281,13 +281,24 @@ static void dp_display_send_hpd_event(struct msm_dp *dp_display)  	drm_helper_hpd_irq_event(connector->dev);  } -static int dp_display_send_hpd_notification(struct dp_display_private *dp, -					    bool hpd) + +static void dp_display_set_encoder_mode(struct dp_display_private *dp)  { -	static bool encoder_mode_set;  	struct msm_drm_private *priv = dp->dp_display.drm_dev->dev_private;  	struct msm_kms *kms = priv->kms; +	if (!dp->encoder_mode_set && dp->dp_display.encoder && +				kms->funcs->set_encoder_mode) { +		kms->funcs->set_encoder_mode(kms, +				dp->dp_display.encoder, false); + +		dp->encoder_mode_set = true; +	} +} + +static int dp_display_send_hpd_notification(struct dp_display_private *dp, +					    bool hpd) +{  	if ((hpd && dp->dp_display.is_connected) ||  			(!hpd && !dp->dp_display.is_connected)) {  		DRM_DEBUG_DP("HPD already %s\n", (hpd ? "on" : "off")); @@ -300,15 +311,6 @@ static int dp_display_send_hpd_notification(struct dp_display_private *dp,  	dp->dp_display.is_connected = hpd; -	if (dp->dp_display.is_connected && dp->dp_display.encoder -				&& !encoder_mode_set -				&& kms->funcs->set_encoder_mode) { -		kms->funcs->set_encoder_mode(kms, -				dp->dp_display.encoder, false); -		DRM_DEBUG_DP("set_encoder_mode() Completed\n"); -		encoder_mode_set = true; -	} -  	dp_display_send_hpd_event(&dp->dp_display);  	return 0; @@ -335,6 +337,7 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)  	dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ;  	dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes; +	dp_link_reset_phy_params_vx_px(dp->link);  	rc = dp_ctrl_on_link(dp->ctrl);  	if (rc) {  		DRM_ERROR("failed to complete DP link training\n"); @@ -343,7 +346,6 @@ static int dp_display_process_hpd_high(struct dp_display_private *dp)  	dp_add_event(dp, EV_USER_NOTIFICATION, true, 0); -  end:  	return rc;  } @@ -360,12 +362,28 @@ static void dp_display_host_init(struct dp_display_private *dp)  	if (dp->usbpd->orientation == ORIENTATION_CC2)  		flip = true; +	dp_display_set_encoder_mode(dp); +  	dp_power_init(dp->power, flip);  	dp_ctrl_host_init(dp->ctrl, flip);  	dp_aux_init(dp->aux);  	dp->core_initialized = true;  } +static void dp_display_host_deinit(struct dp_display_private *dp) +{ +	if (!dp->core_initialized) { +		DRM_DEBUG_DP("DP core not initialized\n"); +		return; +	} + +	dp_ctrl_host_deinit(dp->ctrl); +	dp_aux_deinit(dp->aux); +	dp_power_deinit(dp->power); + +	dp->core_initialized = false; +} +  static int dp_display_usbpd_configure_cb(struct device *dev)  {  	int rc = 0; @@ -429,25 +447,42 @@ static void dp_display_handle_video_request(struct dp_display_private *dp)  	}  } -static int dp_display_handle_irq_hpd(struct dp_display_private *dp) +static int dp_display_handle_port_ststus_changed(struct dp_display_private *dp)  { -	u32 sink_request; - -	sink_request = dp->link->sink_request; +	int rc = 0; -	if (sink_request & DS_PORT_STATUS_CHANGED) { -		dp_add_event(dp, EV_USER_NOTIFICATION, false, 0); -		if (dp_display_is_sink_count_zero(dp)) { -			DRM_DEBUG_DP("sink count is zero, nothing to do\n"); -			return 0; +	if (dp_display_is_sink_count_zero(dp)) { +		DRM_DEBUG_DP("sink count is zero, nothing to do\n"); +		if (dp->hpd_state != ST_DISCONNECTED) { +			dp->hpd_state = ST_DISCONNECT_PENDING; +			dp_add_event(dp, EV_USER_NOTIFICATION, false, 0);  		} +	} else { +		if (dp->hpd_state == ST_DISCONNECTED) { +			dp->hpd_state = ST_CONNECT_PENDING; +			rc = dp_display_process_hpd_high(dp); +			if (rc) +				dp->hpd_state = ST_DISCONNECTED; +		} +	} + +	return rc; +} -		return dp_display_process_hpd_high(dp); +static int dp_display_handle_irq_hpd(struct dp_display_private *dp) +{ +	u32 sink_request = dp->link->sink_request; + +	if (dp->hpd_state == ST_DISCONNECTED) { +		if (sink_request & DP_LINK_STATUS_UPDATED) { +			DRM_ERROR("Disconnected, no DP_LINK_STATUS_UPDATED\n"); +			return -EINVAL; +		}  	}  	dp_ctrl_handle_sink_request(dp->ctrl); -	if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) +	if (sink_request & DP_TEST_LINK_VIDEO_PATTERN)  		dp_display_handle_video_request(dp);  	return 0; @@ -456,7 +491,9 @@ static int dp_display_handle_irq_hpd(struct dp_display_private *dp)  static int dp_display_usbpd_attention_cb(struct device *dev)  {  	int rc = 0; +	u32 sink_request;  	struct dp_display_private *dp; +	struct dp_usbpd *hpd;  	if (!dev) {  		DRM_ERROR("invalid dev\n"); @@ -470,10 +507,17 @@ static int dp_display_usbpd_attention_cb(struct device *dev)  		return -ENODEV;  	} +	hpd = dp->usbpd; +  	/* check for any test request issued by sink */  	rc = dp_link_process_request(dp->link); -	if (!rc) -		dp_display_handle_irq_hpd(dp); +	if (!rc) { +		sink_request = dp->link->sink_request; +		if (sink_request & DS_PORT_STATUS_CHANGED) +			rc = dp_display_handle_port_ststus_changed(dp); +		else +			rc = dp_display_handle_irq_hpd(dp); +	}  	return rc;  } @@ -490,8 +534,8 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)  	mutex_lock(&dp->event_mutex); -	state =  atomic_read(&dp->hpd_state); -	if (state == ST_SUSPEND_PENDING) { +	state =  dp->hpd_state; +	if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) {  		mutex_unlock(&dp->event_mutex);  		return 0;  	} @@ -508,21 +552,23 @@ static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data)  		return 0;  	} -	if (state == ST_SUSPENDED) -		tout = DP_TIMEOUT_NONE; - -	atomic_set(&dp->hpd_state, ST_CONNECT_PENDING); +	dp->hpd_state = ST_CONNECT_PENDING;  	hpd->hpd_high = 1;  	ret = dp_display_usbpd_configure_cb(&dp->pdev->dev); -	if (ret) {	/* failed */ +	if (ret) {	/* link train failed */  		hpd->hpd_high = 0; -		atomic_set(&dp->hpd_state, ST_DISCONNECTED); -	} +		dp->hpd_state = ST_DISCONNECTED; + +		if (ret == -ECONNRESET) { /* cable unplugged */ +			dp->core_initialized = false; +		} -	/* start sanity checking */ -	dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout); +	} else { +		/* start sentinel checking in case of missing uevent */ +		dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout); +	}  	mutex_unlock(&dp->event_mutex); @@ -539,10 +585,10 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)  	mutex_lock(&dp->event_mutex); -	state =  atomic_read(&dp->hpd_state); +	state = dp->hpd_state;  	if (state == ST_CONNECT_PENDING) {  		dp_display_enable(dp, 0); -		atomic_set(&dp->hpd_state, ST_CONNECTED); +		dp->hpd_state = ST_CONNECTED;  	}  	mutex_unlock(&dp->event_mutex); @@ -553,7 +599,14 @@ static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data)  static void dp_display_handle_plugged_change(struct msm_dp *dp_display,  		bool plugged)  { -	if (dp_display->plugged_cb && dp_display->codec_dev) +	struct dp_display_private *dp; + +	dp = container_of(dp_display, +			struct dp_display_private, dp_display); + +	/* notify audio subsystem only if sink supports audio */ +	if (dp_display->plugged_cb && dp_display->codec_dev && +			dp->audio_supported)  		dp_display->plugged_cb(dp_display->codec_dev, plugged);  } @@ -567,12 +620,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)  	mutex_lock(&dp->event_mutex); -	state = atomic_read(&dp->hpd_state); -	if (state == ST_SUSPEND_PENDING) { -		mutex_unlock(&dp->event_mutex); -		return 0; -	} - +	state = dp->hpd_state;  	if (state == ST_DISCONNECT_PENDING || state == ST_DISCONNECTED) {  		mutex_unlock(&dp->event_mutex);  		return 0; @@ -585,7 +633,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)  		return 0;  	} -	atomic_set(&dp->hpd_state, ST_DISCONNECT_PENDING); +	dp->hpd_state = ST_DISCONNECT_PENDING;  	/* disable HPD plug interrupt until disconnect is done */  	dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK @@ -599,7 +647,7 @@ static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data)  	 */  	dp_display_usbpd_disconnect_cb(&dp->pdev->dev); -	/* start sanity checking */ +	/* start sentinel checking in case of missing uevent */  	dp_add_event(dp, EV_DISCONNECT_PENDING_TIMEOUT, 0, DP_TIMEOUT_5_SECOND);  	/* signal the disconnect event early to ensure proper teardown */ @@ -620,10 +668,10 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data  	mutex_lock(&dp->event_mutex); -	state =  atomic_read(&dp->hpd_state); +	state =  dp->hpd_state;  	if (state == ST_DISCONNECT_PENDING) {  		dp_display_disable(dp, 0); -		atomic_set(&dp->hpd_state, ST_DISCONNECTED); +		dp->hpd_state = ST_DISCONNECTED;  	}  	mutex_unlock(&dp->event_mutex); @@ -634,17 +682,21 @@ static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data  static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data)  {  	u32 state; +	int ret;  	mutex_lock(&dp->event_mutex);  	/* irq_hpd can happen at either connected or disconnected state */ -	state =  atomic_read(&dp->hpd_state); -	if (state == ST_SUSPEND_PENDING) { +	state =  dp->hpd_state; +	if (state == ST_DISPLAY_OFF) {  		mutex_unlock(&dp->event_mutex);  		return 0;  	} -	dp_display_usbpd_attention_cb(&dp->pdev->dev); +	ret = dp_display_usbpd_attention_cb(&dp->pdev->dev); +	if (ret == -ECONNRESET) { /* cable unplugged */ +		dp->core_initialized = false; +	}  	mutex_unlock(&dp->event_mutex); @@ -698,7 +750,7 @@ static int dp_init_sub_modules(struct dp_display_private *dp)  		goto error;  	} -	dp->power = dp_power_get(dp->parser); +	dp->power = dp_power_get(dev, dp->parser);  	if (IS_ERR(dp->power)) {  		rc = PTR_ERR(dp->power);  		DRM_ERROR("failed to initialize power, rc = %d\n", rc); @@ -798,8 +850,6 @@ static int dp_display_enable(struct dp_display_private *dp, u32 data)  	if (!rc)  		dp_display->power_on = true; -	/* complete resume_comp regardless it is armed or not */ -	complete(&dp->resume_comp);  	return rc;  } @@ -829,7 +879,7 @@ static int dp_display_disable(struct dp_display_private *dp, u32 data)  	dp_display = g_dp_display;  	if (!dp_display->power_on) -		return -EINVAL; +		return 0;  	/* wait only if audio was enabled */  	if (dp_display->audio_enabled) { @@ -1074,7 +1124,7 @@ static irqreturn_t dp_display_irq_handler(int irq, void *dev_id)  		}  		if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) { -			/* delete connect pending event first */ +			/* stop sentinel connect pending checking */  			dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT);  			dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0);  		} @@ -1151,9 +1201,6 @@ static int dp_display_probe(struct platform_device *pdev)  	}  	mutex_init(&dp->event_mutex); - -	init_completion(&dp->resume_comp); -  	g_dp_display = &dp->dp_display;  	/* Store DP audio handle inside DP display */ @@ -1189,20 +1236,54 @@ static int dp_display_remove(struct platform_device *pdev)  static int dp_pm_resume(struct device *dev)  { +	struct platform_device *pdev = to_platform_device(dev); +	struct msm_dp *dp_display = platform_get_drvdata(pdev); +	struct dp_display_private *dp; +	u32 status; + +	dp = container_of(dp_display, struct dp_display_private, dp_display); + +	mutex_lock(&dp->event_mutex); + +	/* start from disconnected state */ +	dp->hpd_state = ST_DISCONNECTED; + +	/* turn on dp ctrl/phy */ +	dp_display_host_init(dp); + +	dp_catalog_ctrl_hpd_config(dp->catalog); + +	status = dp_catalog_link_is_connected(dp->catalog); + +	if (status) +		dp->dp_display.is_connected = true; +	else +		dp->dp_display.is_connected = false; + +	mutex_unlock(&dp->event_mutex); +  	return 0;  }  static int dp_pm_suspend(struct device *dev)  {  	struct platform_device *pdev = to_platform_device(dev); -	struct dp_display_private *dp = platform_get_drvdata(pdev); +	struct msm_dp *dp_display = platform_get_drvdata(pdev); +	struct dp_display_private *dp; -	if (!dp) { -		DRM_ERROR("DP driver bind failed. Invalid driver data\n"); -		return -EINVAL; -	} +	dp = container_of(dp_display, struct dp_display_private, dp_display); + +	mutex_lock(&dp->event_mutex); + +	if (dp->core_initialized == true) +		dp_display_host_deinit(dp); + +	dp->hpd_state = ST_SUSPENDED; + +	/* host_init will be called at pm_resume */ +	dp->core_initialized = false; -	atomic_set(&dp->hpd_state, ST_SUSPENDED); +	mutex_unlock(&dp->event_mutex);  	return 0;  } @@ -1317,19 +1398,6 @@ int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev,  	return 0;  } -static int dp_display_wait4resume_done(struct dp_display_private *dp) -{ -	int ret = 0; - -	reinit_completion(&dp->resume_comp); -	if (!wait_for_completion_timeout(&dp->resume_comp, -				WAIT_FOR_RESUME_TIMEOUT_JIFFIES)) { -		DRM_ERROR("wait4resume_done timedout\n"); -		ret = -ETIMEDOUT; -	} -	return ret; -} -  int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)  {  	int rc = 0; @@ -1344,6 +1412,9 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)  	mutex_lock(&dp_display->event_mutex); +	/* stop sentinel checking */ +	dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT); +  	rc = dp_display_set_mode(dp, &dp_display->dp_mode);  	if (rc) {  		DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); @@ -1358,15 +1429,10 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)  		return rc;  	} -	state =  atomic_read(&dp_display->hpd_state); -	if (state == ST_SUSPENDED) { -		/* start link training */ -		dp_add_event(dp_display, EV_HPD_PLUG_INT, 0, 0); -		mutex_unlock(&dp_display->event_mutex); +	state =  dp_display->hpd_state; -		/* wait until dp interface is up */ -		goto resume_done; -	} +	if (state == ST_DISPLAY_OFF) +		dp_display_host_init(dp_display);  	dp_display_enable(dp_display, 0); @@ -1377,21 +1443,16 @@ int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder)  		dp_display_unprepare(dp);  	} -	dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT); - -	if (state == ST_SUSPEND_PENDING) +	/* manual kick off plug event to train link */ +	if (state == ST_DISPLAY_OFF)  		dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0);  	/* completed connection */ -	atomic_set(&dp_display->hpd_state, ST_CONNECTED); +	dp_display->hpd_state = ST_CONNECTED;  	mutex_unlock(&dp_display->event_mutex);  	return rc; - -resume_done: -	dp_display_wait4resume_done(dp_display); -	return rc;  }  int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder) @@ -1415,20 +1476,21 @@ int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder)  	mutex_lock(&dp_display->event_mutex); +	/* stop sentinel checking */ +	dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); +  	dp_display_disable(dp_display, 0);  	rc = dp_display_unprepare(dp);  	if (rc)  		DRM_ERROR("DP display unprepare failed, rc=%d\n", rc); -	dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); - -	state =  atomic_read(&dp_display->hpd_state); +	state =  dp_display->hpd_state;  	if (state == ST_DISCONNECT_PENDING) {  		/* completed disconnection */ -		atomic_set(&dp_display->hpd_state, ST_DISCONNECTED); +		dp_display->hpd_state = ST_DISCONNECTED;  	} else { -		atomic_set(&dp_display->hpd_state, ST_SUSPEND_PENDING); +		dp_display->hpd_state = ST_DISPLAY_OFF;  	}  	mutex_unlock(&dp_display->event_mutex); | 
