diff options
Diffstat (limited to 'drivers/gpu/drm/drm_dp_mst_topology.c')
| -rw-r--r-- | drivers/gpu/drm/drm_dp_mst_topology.c | 181 | 
1 files changed, 117 insertions, 64 deletions
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index 273dd80fabf3..e6afe4faeca6 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -393,7 +393,7 @@ drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,  			memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);  			idx += req->u.i2c_read.transactions[i].num_bytes; -			buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5; +			buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 4;  			buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);  			idx++;  		} @@ -1190,6 +1190,8 @@ static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,  		    txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {  			mstb->tx_slots[txmsg->seqno] = NULL;  		} +		mgr->is_waiting_for_dwn_reply = false; +  	}  out:  	if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) { @@ -1199,6 +1201,7 @@ out:  	}  	mutex_unlock(&mgr->qlock); +	drm_dp_mst_kick_tx(mgr);  	return ret;  } @@ -1913,73 +1916,90 @@ static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,  	return parent_lct + 1;  } -static int drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt) +static bool drm_dp_mst_is_dp_mst_end_device(u8 pdt, bool mcs) +{ +	switch (pdt) { +	case DP_PEER_DEVICE_DP_LEGACY_CONV: +	case DP_PEER_DEVICE_SST_SINK: +		return true; +	case DP_PEER_DEVICE_MST_BRANCHING: +		/* For sst branch device */ +		if (!mcs) +			return true; + +		return false; +	} +	return true; +} + +static int +drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt, +		    bool new_mcs)  {  	struct drm_dp_mst_topology_mgr *mgr = port->mgr;  	struct drm_dp_mst_branch *mstb;  	u8 rad[8], lct;  	int ret = 0; -	if (port->pdt == new_pdt) +	if (port->pdt == new_pdt && port->mcs == new_mcs)  		return 0;  	/* Teardown the old pdt, if there is one */ -	switch (port->pdt) { -	case DP_PEER_DEVICE_DP_LEGACY_CONV: -	case DP_PEER_DEVICE_SST_SINK: -		/* -		 * If the new PDT would also have an i2c bus, don't bother -		 * with reregistering it -		 */ -		if (new_pdt == DP_PEER_DEVICE_DP_LEGACY_CONV || -		    new_pdt == DP_PEER_DEVICE_SST_SINK) { -			port->pdt = new_pdt; -			return 0; -		} +	if (port->pdt != DP_PEER_DEVICE_NONE) { +		if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) { +			/* +			 * If the new PDT would also have an i2c bus, +			 * don't bother with reregistering it +			 */ +			if (new_pdt != DP_PEER_DEVICE_NONE && +			    drm_dp_mst_is_dp_mst_end_device(new_pdt, new_mcs)) { +				port->pdt = new_pdt; +				port->mcs = new_mcs; +				return 0; +			} -		/* remove i2c over sideband */ -		drm_dp_mst_unregister_i2c_bus(&port->aux); -		break; -	case DP_PEER_DEVICE_MST_BRANCHING: -		mutex_lock(&mgr->lock); -		drm_dp_mst_topology_put_mstb(port->mstb); -		port->mstb = NULL; -		mutex_unlock(&mgr->lock); -		break; +			/* remove i2c over sideband */ +			drm_dp_mst_unregister_i2c_bus(&port->aux); +		} else { +			mutex_lock(&mgr->lock); +			drm_dp_mst_topology_put_mstb(port->mstb); +			port->mstb = NULL; +			mutex_unlock(&mgr->lock); +		}  	}  	port->pdt = new_pdt; -	switch (port->pdt) { -	case DP_PEER_DEVICE_DP_LEGACY_CONV: -	case DP_PEER_DEVICE_SST_SINK: -		/* add i2c over sideband */ -		ret = drm_dp_mst_register_i2c_bus(&port->aux); -		break; +	port->mcs = new_mcs; -	case DP_PEER_DEVICE_MST_BRANCHING: -		lct = drm_dp_calculate_rad(port, rad); -		mstb = drm_dp_add_mst_branch_device(lct, rad); -		if (!mstb) { -			ret = -ENOMEM; -			DRM_ERROR("Failed to create MSTB for port %p", port); -			goto out; -		} +	if (port->pdt != DP_PEER_DEVICE_NONE) { +		if (drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) { +			/* add i2c over sideband */ +			ret = drm_dp_mst_register_i2c_bus(&port->aux); +		} else { +			lct = drm_dp_calculate_rad(port, rad); +			mstb = drm_dp_add_mst_branch_device(lct, rad); +			if (!mstb) { +				ret = -ENOMEM; +				DRM_ERROR("Failed to create MSTB for port %p", +					  port); +				goto out; +			} -		mutex_lock(&mgr->lock); -		port->mstb = mstb; -		mstb->mgr = port->mgr; -		mstb->port_parent = port; +			mutex_lock(&mgr->lock); +			port->mstb = mstb; +			mstb->mgr = port->mgr; +			mstb->port_parent = port; -		/* -		 * Make sure this port's memory allocation stays -		 * around until its child MSTB releases it -		 */ -		drm_dp_mst_get_port_malloc(port); -		mutex_unlock(&mgr->lock); +			/* +			 * Make sure this port's memory allocation stays +			 * around until its child MSTB releases it +			 */ +			drm_dp_mst_get_port_malloc(port); +			mutex_unlock(&mgr->lock); -		/* And make sure we send a link address for this */ -		ret = 1; -		break; +			/* And make sure we send a link address for this */ +			ret = 1; +		}  	}  out: @@ -2132,9 +2152,8 @@ drm_dp_mst_port_add_connector(struct drm_dp_mst_branch *mstb,  		goto error;  	} -	if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV || -	     port->pdt == DP_PEER_DEVICE_SST_SINK) && -	    port->port_num >= DP_MST_LOGICAL_PORT_0) { +	if (port->pdt != DP_PEER_DEVICE_NONE && +	    drm_dp_mst_is_dp_mst_end_device(port->pdt, port->mcs)) {  		port->cached_edid = drm_get_edid(port->connector,  						 &port->aux.ddc);  		drm_connector_set_tile_property(port->connector); @@ -2198,6 +2217,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,  	struct drm_dp_mst_port *port;  	int old_ddps = 0, ret;  	u8 new_pdt = DP_PEER_DEVICE_NONE; +	bool new_mcs = 0;  	bool created = false, send_link_addr = false, changed = false;  	port = drm_dp_get_port(mstb, port_msg->port_number); @@ -2242,7 +2262,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,  	port->input = port_msg->input_port;  	if (!port->input)  		new_pdt = port_msg->peer_device_type; -	port->mcs = port_msg->mcs; +	new_mcs = port_msg->mcs;  	port->ddps = port_msg->ddps;  	port->ldps = port_msg->legacy_device_plug_status;  	port->dpcd_rev = port_msg->dpcd_revision; @@ -2269,7 +2289,7 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,  		}  	} -	ret = drm_dp_port_set_pdt(port, new_pdt); +	ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);  	if (ret == 1) {  		send_link_addr = true;  	} else if (ret < 0) { @@ -2283,7 +2303,8 @@ drm_dp_mst_handle_link_address_port(struct drm_dp_mst_branch *mstb,  	 * we're coming out of suspend. In this case, always resend the link  	 * address if there's an MSTB on this port  	 */ -	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING) +	if (!created && port->pdt == DP_PEER_DEVICE_MST_BRANCHING && +	    port->mcs)  		send_link_addr = true;  	if (port->connector) @@ -2318,8 +2339,9 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,  {  	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;  	struct drm_dp_mst_port *port; -	int old_ddps, ret; +	int old_ddps, old_input, ret, i;  	u8 new_pdt; +	bool new_mcs;  	bool dowork = false, create_connector = false;  	port = drm_dp_get_port(mstb, conn_stat->port_number); @@ -2349,8 +2371,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,  	}  	old_ddps = port->ddps; +	old_input = port->input;  	port->input = conn_stat->input_port; -	port->mcs = conn_stat->message_capability_status;  	port->ldps = conn_stat->legacy_device_plug_status;  	port->ddps = conn_stat->displayport_device_plug_status; @@ -2363,8 +2385,8 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,  	}  	new_pdt = port->input ? DP_PEER_DEVICE_NONE : conn_stat->peer_device_type; - -	ret = drm_dp_port_set_pdt(port, new_pdt); +	new_mcs = conn_stat->message_capability_status; +	ret = drm_dp_port_set_pdt(port, new_pdt, new_mcs);  	if (ret == 1) {  		dowork = true;  	} else if (ret < 0) { @@ -2373,6 +2395,28 @@ drm_dp_mst_handle_conn_stat(struct drm_dp_mst_branch *mstb,  		dowork = false;  	} +	if (!old_input && old_ddps != port->ddps && !port->ddps) { +		for (i = 0; i < mgr->max_payloads; i++) { +			struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i]; +			struct drm_dp_mst_port *port_validated; + +			if (!vcpi) +				continue; + +			port_validated = +				container_of(vcpi, struct drm_dp_mst_port, vcpi); +			port_validated = +				drm_dp_mst_topology_get_port_validated(mgr, port_validated); +			if (!port_validated) { +				mutex_lock(&mgr->payload_lock); +				vcpi->num_slots = 0; +				mutex_unlock(&mgr->payload_lock); +			} else { +				drm_dp_mst_topology_put_port(port_validated); +			} +		} +	} +  	if (port->connector)  		drm_modeset_unlock(&mgr->base.lock);  	else if (create_connector) @@ -2718,9 +2762,11 @@ static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)  	ret = process_single_tx_qlock(mgr, txmsg, false);  	if (ret == 1) {  		/* txmsg is sent it should be in the slots now */ +		mgr->is_waiting_for_dwn_reply = true;  		list_del(&txmsg->next);  	} else if (ret) {  		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret); +		mgr->is_waiting_for_dwn_reply = false;  		list_del(&txmsg->next);  		if (txmsg->seqno != -1)  			txmsg->dst->tx_slots[txmsg->seqno] = NULL; @@ -2760,7 +2806,8 @@ static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,  		drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);  	} -	if (list_is_singular(&mgr->tx_msg_downq)) +	if (list_is_singular(&mgr->tx_msg_downq) && +	    !mgr->is_waiting_for_dwn_reply)  		process_single_down_tx_qlock(mgr);  	mutex_unlock(&mgr->qlock);  } @@ -3678,6 +3725,7 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)  	mutex_lock(&mgr->qlock);  	txmsg->state = DRM_DP_SIDEBAND_TX_RX;  	mstb->tx_slots[slot] = NULL; +	mgr->is_waiting_for_dwn_reply = false;  	mutex_unlock(&mgr->qlock);  	wake_up_all(&mgr->tx_waitq); @@ -3687,6 +3735,9 @@ static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)  no_msg:  	drm_dp_mst_topology_put_mstb(mstb);  clear_down_rep_recv: +	mutex_lock(&mgr->qlock); +	mgr->is_waiting_for_dwn_reply = false; +	mutex_unlock(&mgr->qlock);  	memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));  	return 0; @@ -3896,6 +3947,8 @@ drm_dp_mst_detect_port(struct drm_connector *connector,  	switch (port->pdt) {  	case DP_PEER_DEVICE_NONE:  	case DP_PEER_DEVICE_MST_BRANCHING: +		if (!port->mcs) +			ret = connector_status_connected;  		break;  	case DP_PEER_DEVICE_SST_SINK: @@ -4497,7 +4550,7 @@ static void drm_dp_tx_work(struct work_struct *work)  	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);  	mutex_lock(&mgr->qlock); -	if (!list_empty(&mgr->tx_msg_downq)) +	if (!list_empty(&mgr->tx_msg_downq) && !mgr->is_waiting_for_dwn_reply)  		process_single_down_tx_qlock(mgr);  	mutex_unlock(&mgr->qlock);  } @@ -4508,7 +4561,7 @@ drm_dp_delayed_destroy_port(struct drm_dp_mst_port *port)  	if (port->connector)  		port->mgr->cbs->destroy_connector(port->mgr, port->connector); -	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE); +	drm_dp_port_set_pdt(port, DP_PEER_DEVICE_NONE, port->mcs);  	drm_dp_mst_put_port_malloc(port);  }  | 

