xref: /dflybsd-src/sys/dev/drm/drm_dp_mst_topology.c (revision 3f2dd94a569761201b5b0a18b2f697f97fe1b9dc)
124edb884SFrançois Tigeot /*
224edb884SFrançois Tigeot  * Copyright © 2014 Red Hat
324edb884SFrançois Tigeot  *
424edb884SFrançois Tigeot  * Permission to use, copy, modify, distribute, and sell this software and its
524edb884SFrançois Tigeot  * documentation for any purpose is hereby granted without fee, provided that
624edb884SFrançois Tigeot  * the above copyright notice appear in all copies and that both that copyright
724edb884SFrançois Tigeot  * notice and this permission notice appear in supporting documentation, and
824edb884SFrançois Tigeot  * that the name of the copyright holders not be used in advertising or
924edb884SFrançois Tigeot  * publicity pertaining to distribution of the software without specific,
1024edb884SFrançois Tigeot  * written prior permission.  The copyright holders make no representations
1124edb884SFrançois Tigeot  * about the suitability of this software for any purpose.  It is provided "as
1224edb884SFrançois Tigeot  * is" without express or implied warranty.
1324edb884SFrançois Tigeot  *
1424edb884SFrançois Tigeot  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
1524edb884SFrançois Tigeot  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
1624edb884SFrançois Tigeot  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
1724edb884SFrançois Tigeot  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
1824edb884SFrançois Tigeot  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
1924edb884SFrançois Tigeot  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
2024edb884SFrançois Tigeot  * OF THIS SOFTWARE.
2124edb884SFrançois Tigeot  */
2224edb884SFrançois Tigeot 
2324edb884SFrançois Tigeot #include <linux/kernel.h>
2424edb884SFrançois Tigeot #include <linux/delay.h>
25*3f2dd94aSFrançois Tigeot #include <linux/init.h>
2624edb884SFrançois Tigeot #include <linux/errno.h>
2724edb884SFrançois Tigeot #include <linux/sched.h>
2824edb884SFrançois Tigeot #include <linux/seq_file.h>
2924edb884SFrançois Tigeot #include <linux/i2c.h>
3024edb884SFrançois Tigeot #include <drm/drm_dp_mst_helper.h>
3124edb884SFrançois Tigeot #include <drm/drmP.h>
3224edb884SFrançois Tigeot 
3324edb884SFrançois Tigeot #include <drm/drm_fixed.h>
34*3f2dd94aSFrançois Tigeot #include <drm/drm_atomic.h>
35*3f2dd94aSFrançois Tigeot #include <drm/drm_atomic_helper.h>
3624edb884SFrançois Tigeot 
3724edb884SFrançois Tigeot /**
3824edb884SFrançois Tigeot  * DOC: dp mst helper
3924edb884SFrançois Tigeot  *
4024edb884SFrançois Tigeot  * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
4124edb884SFrançois Tigeot  * protocol. The helpers contain a topology manager and bandwidth manager.
4224edb884SFrançois Tigeot  * The helpers encapsulate the sending and received of sideband msgs.
4324edb884SFrançois Tigeot  */
4424edb884SFrançois Tigeot static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
4524edb884SFrançois Tigeot 				  char *buf);
4624edb884SFrançois Tigeot static int test_calc_pbn_mode(void);
4724edb884SFrançois Tigeot 
4824edb884SFrançois Tigeot static void drm_dp_put_port(struct drm_dp_mst_port *port);
4924edb884SFrançois Tigeot 
5024edb884SFrançois Tigeot static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
5124edb884SFrançois Tigeot 				     int id,
5224edb884SFrançois Tigeot 				     struct drm_dp_payload *payload);
5324edb884SFrançois Tigeot 
5424edb884SFrançois Tigeot static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
5524edb884SFrançois Tigeot 				  struct drm_dp_mst_port *port,
5624edb884SFrançois Tigeot 				  int offset, int size, u8 *bytes);
5724edb884SFrançois Tigeot 
58a05eeebfSFrançois Tigeot static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
5924edb884SFrançois Tigeot 				     struct drm_dp_mst_branch *mstb);
6024edb884SFrançois Tigeot static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
6124edb884SFrançois Tigeot 					   struct drm_dp_mst_branch *mstb,
6224edb884SFrançois Tigeot 					   struct drm_dp_mst_port *port);
6324edb884SFrançois Tigeot static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
6424edb884SFrançois Tigeot 				 u8 *guid);
6524edb884SFrançois Tigeot 
6624edb884SFrançois Tigeot static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux);
6724edb884SFrançois Tigeot static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux);
6824edb884SFrançois Tigeot static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);
6924edb884SFrançois Tigeot /* sideband msg handling */
drm_dp_msg_header_crc4(const uint8_t * data,size_t num_nibbles)7024edb884SFrançois Tigeot static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
7124edb884SFrançois Tigeot {
7224edb884SFrançois Tigeot 	u8 bitmask = 0x80;
7324edb884SFrançois Tigeot 	u8 bitshift = 7;
7424edb884SFrançois Tigeot 	u8 array_index = 0;
7524edb884SFrançois Tigeot 	int number_of_bits = num_nibbles * 4;
7624edb884SFrançois Tigeot 	u8 remainder = 0;
7724edb884SFrançois Tigeot 
7824edb884SFrançois Tigeot 	while (number_of_bits != 0) {
7924edb884SFrançois Tigeot 		number_of_bits--;
8024edb884SFrançois Tigeot 		remainder <<= 1;
8124edb884SFrançois Tigeot 		remainder |= (data[array_index] & bitmask) >> bitshift;
8224edb884SFrançois Tigeot 		bitmask >>= 1;
8324edb884SFrançois Tigeot 		bitshift--;
8424edb884SFrançois Tigeot 		if (bitmask == 0) {
8524edb884SFrançois Tigeot 			bitmask = 0x80;
8624edb884SFrançois Tigeot 			bitshift = 7;
8724edb884SFrançois Tigeot 			array_index++;
8824edb884SFrançois Tigeot 		}
8924edb884SFrançois Tigeot 		if ((remainder & 0x10) == 0x10)
9024edb884SFrançois Tigeot 			remainder ^= 0x13;
9124edb884SFrançois Tigeot 	}
9224edb884SFrançois Tigeot 
9324edb884SFrançois Tigeot 	number_of_bits = 4;
9424edb884SFrançois Tigeot 	while (number_of_bits != 0) {
9524edb884SFrançois Tigeot 		number_of_bits--;
9624edb884SFrançois Tigeot 		remainder <<= 1;
9724edb884SFrançois Tigeot 		if ((remainder & 0x10) != 0)
9824edb884SFrançois Tigeot 			remainder ^= 0x13;
9924edb884SFrançois Tigeot 	}
10024edb884SFrançois Tigeot 
10124edb884SFrançois Tigeot 	return remainder;
10224edb884SFrançois Tigeot }
10324edb884SFrançois Tigeot 
drm_dp_msg_data_crc4(const uint8_t * data,u8 number_of_bytes)10424edb884SFrançois Tigeot static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
10524edb884SFrançois Tigeot {
10624edb884SFrançois Tigeot 	u8 bitmask = 0x80;
10724edb884SFrançois Tigeot 	u8 bitshift = 7;
10824edb884SFrançois Tigeot 	u8 array_index = 0;
10924edb884SFrançois Tigeot 	int number_of_bits = number_of_bytes * 8;
11024edb884SFrançois Tigeot 	u16 remainder = 0;
11124edb884SFrançois Tigeot 
11224edb884SFrançois Tigeot 	while (number_of_bits != 0) {
11324edb884SFrançois Tigeot 		number_of_bits--;
11424edb884SFrançois Tigeot 		remainder <<= 1;
11524edb884SFrançois Tigeot 		remainder |= (data[array_index] & bitmask) >> bitshift;
11624edb884SFrançois Tigeot 		bitmask >>= 1;
11724edb884SFrançois Tigeot 		bitshift--;
11824edb884SFrançois Tigeot 		if (bitmask == 0) {
11924edb884SFrançois Tigeot 			bitmask = 0x80;
12024edb884SFrançois Tigeot 			bitshift = 7;
12124edb884SFrançois Tigeot 			array_index++;
12224edb884SFrançois Tigeot 		}
12324edb884SFrançois Tigeot 		if ((remainder & 0x100) == 0x100)
12424edb884SFrançois Tigeot 			remainder ^= 0xd5;
12524edb884SFrançois Tigeot 	}
12624edb884SFrançois Tigeot 
12724edb884SFrançois Tigeot 	number_of_bits = 8;
12824edb884SFrançois Tigeot 	while (number_of_bits != 0) {
12924edb884SFrançois Tigeot 		number_of_bits--;
13024edb884SFrançois Tigeot 		remainder <<= 1;
13124edb884SFrançois Tigeot 		if ((remainder & 0x100) != 0)
13224edb884SFrançois Tigeot 			remainder ^= 0xd5;
13324edb884SFrançois Tigeot 	}
13424edb884SFrançois Tigeot 
13524edb884SFrançois Tigeot 	return remainder & 0xff;
13624edb884SFrançois Tigeot }
drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr * hdr)13724edb884SFrançois Tigeot static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
13824edb884SFrançois Tigeot {
13924edb884SFrançois Tigeot 	u8 size = 3;
14024edb884SFrançois Tigeot 	size += (hdr->lct / 2);
14124edb884SFrançois Tigeot 	return size;
14224edb884SFrançois Tigeot }
14324edb884SFrançois Tigeot 
drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr * hdr,u8 * buf,int * len)14424edb884SFrançois Tigeot static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
14524edb884SFrançois Tigeot 					   u8 *buf, int *len)
14624edb884SFrançois Tigeot {
14724edb884SFrançois Tigeot 	int idx = 0;
14824edb884SFrançois Tigeot 	int i;
14924edb884SFrançois Tigeot 	u8 crc4;
15024edb884SFrançois Tigeot 	buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
15124edb884SFrançois Tigeot 	for (i = 0; i < (hdr->lct / 2); i++)
15224edb884SFrançois Tigeot 		buf[idx++] = hdr->rad[i];
15324edb884SFrançois Tigeot 	buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
15424edb884SFrançois Tigeot 		(hdr->msg_len & 0x3f);
15524edb884SFrançois Tigeot 	buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);
15624edb884SFrançois Tigeot 
15724edb884SFrançois Tigeot 	crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
15824edb884SFrançois Tigeot 	buf[idx - 1] |= (crc4 & 0xf);
15924edb884SFrançois Tigeot 
16024edb884SFrançois Tigeot 	*len = idx;
16124edb884SFrançois Tigeot }
16224edb884SFrançois Tigeot 
drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr * hdr,u8 * buf,int buflen,u8 * hdrlen)16324edb884SFrançois Tigeot static bool drm_dp_decode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
16424edb884SFrançois Tigeot 					   u8 *buf, int buflen, u8 *hdrlen)
16524edb884SFrançois Tigeot {
16624edb884SFrançois Tigeot 	u8 crc4;
16724edb884SFrançois Tigeot 	u8 len;
16824edb884SFrançois Tigeot 	int i;
16924edb884SFrançois Tigeot 	u8 idx;
17024edb884SFrançois Tigeot 	if (buf[0] == 0)
17124edb884SFrançois Tigeot 		return false;
17224edb884SFrançois Tigeot 	len = 3;
17324edb884SFrançois Tigeot 	len += ((buf[0] & 0xf0) >> 4) / 2;
17424edb884SFrançois Tigeot 	if (len > buflen)
17524edb884SFrançois Tigeot 		return false;
17624edb884SFrançois Tigeot 	crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);
17724edb884SFrançois Tigeot 
17824edb884SFrançois Tigeot 	if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
17924edb884SFrançois Tigeot 		DRM_DEBUG_KMS("crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
18024edb884SFrançois Tigeot 		return false;
18124edb884SFrançois Tigeot 	}
18224edb884SFrançois Tigeot 
18324edb884SFrançois Tigeot 	hdr->lct = (buf[0] & 0xf0) >> 4;
18424edb884SFrançois Tigeot 	hdr->lcr = (buf[0] & 0xf);
18524edb884SFrançois Tigeot 	idx = 1;
18624edb884SFrançois Tigeot 	for (i = 0; i < (hdr->lct / 2); i++)
18724edb884SFrançois Tigeot 		hdr->rad[i] = buf[idx++];
18824edb884SFrançois Tigeot 	hdr->broadcast = (buf[idx] >> 7) & 0x1;
18924edb884SFrançois Tigeot 	hdr->path_msg = (buf[idx] >> 6) & 0x1;
19024edb884SFrançois Tigeot 	hdr->msg_len = buf[idx] & 0x3f;
19124edb884SFrançois Tigeot 	idx++;
19224edb884SFrançois Tigeot 	hdr->somt = (buf[idx] >> 7) & 0x1;
19324edb884SFrançois Tigeot 	hdr->eomt = (buf[idx] >> 6) & 0x1;
19424edb884SFrançois Tigeot 	hdr->seqno = (buf[idx] >> 4) & 0x1;
19524edb884SFrançois Tigeot 	idx++;
19624edb884SFrançois Tigeot 	*hdrlen = idx;
19724edb884SFrançois Tigeot 	return true;
19824edb884SFrançois Tigeot }
19924edb884SFrançois Tigeot 
drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body * req,struct drm_dp_sideband_msg_tx * raw)20024edb884SFrançois Tigeot static void drm_dp_encode_sideband_req(struct drm_dp_sideband_msg_req_body *req,
20124edb884SFrançois Tigeot 				       struct drm_dp_sideband_msg_tx *raw)
20224edb884SFrançois Tigeot {
20324edb884SFrançois Tigeot 	int idx = 0;
20424edb884SFrançois Tigeot 	int i;
20524edb884SFrançois Tigeot 	u8 *buf = raw->msg;
20624edb884SFrançois Tigeot 	buf[idx++] = req->req_type & 0x7f;
20724edb884SFrançois Tigeot 
20824edb884SFrançois Tigeot 	switch (req->req_type) {
20924edb884SFrançois Tigeot 	case DP_ENUM_PATH_RESOURCES:
21024edb884SFrançois Tigeot 		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
21124edb884SFrançois Tigeot 		idx++;
21224edb884SFrançois Tigeot 		break;
21324edb884SFrançois Tigeot 	case DP_ALLOCATE_PAYLOAD:
21424edb884SFrançois Tigeot 		buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
21524edb884SFrançois Tigeot 			(req->u.allocate_payload.number_sdp_streams & 0xf);
21624edb884SFrançois Tigeot 		idx++;
21724edb884SFrançois Tigeot 		buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
21824edb884SFrançois Tigeot 		idx++;
21924edb884SFrançois Tigeot 		buf[idx] = (req->u.allocate_payload.pbn >> 8);
22024edb884SFrançois Tigeot 		idx++;
22124edb884SFrançois Tigeot 		buf[idx] = (req->u.allocate_payload.pbn & 0xff);
22224edb884SFrançois Tigeot 		idx++;
22324edb884SFrançois Tigeot 		for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
22424edb884SFrançois Tigeot 			buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
22524edb884SFrançois Tigeot 				(req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
22624edb884SFrançois Tigeot 			idx++;
22724edb884SFrançois Tigeot 		}
22824edb884SFrançois Tigeot 		if (req->u.allocate_payload.number_sdp_streams & 1) {
22924edb884SFrançois Tigeot 			i = req->u.allocate_payload.number_sdp_streams - 1;
23024edb884SFrançois Tigeot 			buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
23124edb884SFrançois Tigeot 			idx++;
23224edb884SFrançois Tigeot 		}
23324edb884SFrançois Tigeot 		break;
23424edb884SFrançois Tigeot 	case DP_QUERY_PAYLOAD:
23524edb884SFrançois Tigeot 		buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
23624edb884SFrançois Tigeot 		idx++;
23724edb884SFrançois Tigeot 		buf[idx] = (req->u.query_payload.vcpi & 0x7f);
23824edb884SFrançois Tigeot 		idx++;
23924edb884SFrançois Tigeot 		break;
24024edb884SFrançois Tigeot 	case DP_REMOTE_DPCD_READ:
24124edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
24224edb884SFrançois Tigeot 		buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
24324edb884SFrançois Tigeot 		idx++;
24424edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
24524edb884SFrançois Tigeot 		idx++;
24624edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
24724edb884SFrançois Tigeot 		idx++;
24824edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_read.num_bytes);
24924edb884SFrançois Tigeot 		idx++;
25024edb884SFrançois Tigeot 		break;
25124edb884SFrançois Tigeot 
25224edb884SFrançois Tigeot 	case DP_REMOTE_DPCD_WRITE:
25324edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
25424edb884SFrançois Tigeot 		buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
25524edb884SFrançois Tigeot 		idx++;
25624edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
25724edb884SFrançois Tigeot 		idx++;
25824edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
25924edb884SFrançois Tigeot 		idx++;
26024edb884SFrançois Tigeot 		buf[idx] = (req->u.dpcd_write.num_bytes);
26124edb884SFrançois Tigeot 		idx++;
26224edb884SFrançois Tigeot 		memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
26324edb884SFrançois Tigeot 		idx += req->u.dpcd_write.num_bytes;
26424edb884SFrançois Tigeot 		break;
26524edb884SFrançois Tigeot 	case DP_REMOTE_I2C_READ:
26624edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
26724edb884SFrançois Tigeot 		buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
26824edb884SFrançois Tigeot 		idx++;
26924edb884SFrançois Tigeot 		for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
27024edb884SFrançois Tigeot 			buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
27124edb884SFrançois Tigeot 			idx++;
27224edb884SFrançois Tigeot 			buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
27324edb884SFrançois Tigeot 			idx++;
27424edb884SFrançois Tigeot 			memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
27524edb884SFrançois Tigeot 			idx += req->u.i2c_read.transactions[i].num_bytes;
27624edb884SFrançois Tigeot 
27724edb884SFrançois Tigeot 			buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 5;
27824edb884SFrançois Tigeot 			buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
27924edb884SFrançois Tigeot 			idx++;
28024edb884SFrançois Tigeot 		}
28124edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
28224edb884SFrançois Tigeot 		idx++;
28324edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_read.num_bytes_read);
28424edb884SFrançois Tigeot 		idx++;
28524edb884SFrançois Tigeot 		break;
28624edb884SFrançois Tigeot 
28724edb884SFrançois Tigeot 	case DP_REMOTE_I2C_WRITE:
28824edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
28924edb884SFrançois Tigeot 		idx++;
29024edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
29124edb884SFrançois Tigeot 		idx++;
29224edb884SFrançois Tigeot 		buf[idx] = (req->u.i2c_write.num_bytes);
29324edb884SFrançois Tigeot 		idx++;
29424edb884SFrançois Tigeot 		memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
29524edb884SFrançois Tigeot 		idx += req->u.i2c_write.num_bytes;
29624edb884SFrançois Tigeot 		break;
297*3f2dd94aSFrançois Tigeot 
298*3f2dd94aSFrançois Tigeot 	case DP_POWER_DOWN_PHY:
299*3f2dd94aSFrançois Tigeot 	case DP_POWER_UP_PHY:
300*3f2dd94aSFrançois Tigeot 		buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
301*3f2dd94aSFrançois Tigeot 		idx++;
302*3f2dd94aSFrançois Tigeot 		break;
30324edb884SFrançois Tigeot 	}
30424edb884SFrançois Tigeot 	raw->cur_len = idx;
30524edb884SFrançois Tigeot }
30624edb884SFrançois Tigeot 
drm_dp_crc_sideband_chunk_req(u8 * msg,u8 len)30724edb884SFrançois Tigeot static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
30824edb884SFrançois Tigeot {
30924edb884SFrançois Tigeot 	u8 crc4;
31024edb884SFrançois Tigeot 	crc4 = drm_dp_msg_data_crc4(msg, len);
31124edb884SFrançois Tigeot 	msg[len] = crc4;
31224edb884SFrançois Tigeot }
31324edb884SFrançois Tigeot 
drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body * rep,struct drm_dp_sideband_msg_tx * raw)31424edb884SFrançois Tigeot static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
31524edb884SFrançois Tigeot 					 struct drm_dp_sideband_msg_tx *raw)
31624edb884SFrançois Tigeot {
31724edb884SFrançois Tigeot 	int idx = 0;
31824edb884SFrançois Tigeot 	u8 *buf = raw->msg;
31924edb884SFrançois Tigeot 
32024edb884SFrançois Tigeot 	buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);
32124edb884SFrançois Tigeot 
32224edb884SFrançois Tigeot 	raw->cur_len = idx;
32324edb884SFrançois Tigeot }
32424edb884SFrançois Tigeot 
32524edb884SFrançois Tigeot /* this adds a chunk of msg to the builder to get the final msg */
drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx * msg,u8 * replybuf,u8 replybuflen,bool hdr)32624edb884SFrançois Tigeot static bool drm_dp_sideband_msg_build(struct drm_dp_sideband_msg_rx *msg,
32724edb884SFrançois Tigeot 				      u8 *replybuf, u8 replybuflen, bool hdr)
32824edb884SFrançois Tigeot {
32924edb884SFrançois Tigeot 	int ret;
33024edb884SFrançois Tigeot 	u8 crc4;
33124edb884SFrançois Tigeot 
33224edb884SFrançois Tigeot 	if (hdr) {
33324edb884SFrançois Tigeot 		u8 hdrlen;
33424edb884SFrançois Tigeot 		struct drm_dp_sideband_msg_hdr recv_hdr;
33524edb884SFrançois Tigeot 		ret = drm_dp_decode_sideband_msg_hdr(&recv_hdr, replybuf, replybuflen, &hdrlen);
33624edb884SFrançois Tigeot 		if (ret == false) {
33724edb884SFrançois Tigeot 			print_hex_dump(KERN_DEBUG, "failed hdr", DUMP_PREFIX_NONE, 16, 1, replybuf, replybuflen, false);
33824edb884SFrançois Tigeot 			return false;
33924edb884SFrançois Tigeot 		}
34024edb884SFrançois Tigeot 
341a85cb24fSFrançois Tigeot 		/*
342a85cb24fSFrançois Tigeot 		 * ignore out-of-order messages or messages that are part of a
343a85cb24fSFrançois Tigeot 		 * failed transaction
344a85cb24fSFrançois Tigeot 		 */
345a85cb24fSFrançois Tigeot 		if (!recv_hdr.somt && !msg->have_somt)
346a85cb24fSFrançois Tigeot 			return false;
347a85cb24fSFrançois Tigeot 
34824edb884SFrançois Tigeot 		/* get length contained in this portion */
34924edb884SFrançois Tigeot 		msg->curchunk_len = recv_hdr.msg_len;
35024edb884SFrançois Tigeot 		msg->curchunk_hdrlen = hdrlen;
35124edb884SFrançois Tigeot 
35224edb884SFrançois Tigeot 		/* we have already gotten an somt - don't bother parsing */
35324edb884SFrançois Tigeot 		if (recv_hdr.somt && msg->have_somt)
35424edb884SFrançois Tigeot 			return false;
35524edb884SFrançois Tigeot 
35624edb884SFrançois Tigeot 		if (recv_hdr.somt) {
35724edb884SFrançois Tigeot 			memcpy(&msg->initial_hdr, &recv_hdr, sizeof(struct drm_dp_sideband_msg_hdr));
35824edb884SFrançois Tigeot 			msg->have_somt = true;
35924edb884SFrançois Tigeot 		}
36024edb884SFrançois Tigeot 		if (recv_hdr.eomt)
36124edb884SFrançois Tigeot 			msg->have_eomt = true;
36224edb884SFrançois Tigeot 
36324edb884SFrançois Tigeot 		/* copy the bytes for the remainder of this header chunk */
36424edb884SFrançois Tigeot 		msg->curchunk_idx = min(msg->curchunk_len, (u8)(replybuflen - hdrlen));
36524edb884SFrançois Tigeot 		memcpy(&msg->chunk[0], replybuf + hdrlen, msg->curchunk_idx);
36624edb884SFrançois Tigeot 	} else {
36724edb884SFrançois Tigeot 		memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
36824edb884SFrançois Tigeot 		msg->curchunk_idx += replybuflen;
36924edb884SFrançois Tigeot 	}
37024edb884SFrançois Tigeot 
37124edb884SFrançois Tigeot 	if (msg->curchunk_idx >= msg->curchunk_len) {
37224edb884SFrançois Tigeot 		/* do CRC */
37324edb884SFrançois Tigeot 		crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
37424edb884SFrançois Tigeot 		/* copy chunk into bigger msg */
37524edb884SFrançois Tigeot 		memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
37624edb884SFrançois Tigeot 		msg->curlen += msg->curchunk_len - 1;
37724edb884SFrançois Tigeot 	}
37824edb884SFrançois Tigeot 	return true;
37924edb884SFrançois Tigeot }
38024edb884SFrançois Tigeot 
drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)38124edb884SFrançois Tigeot static bool drm_dp_sideband_parse_link_address(struct drm_dp_sideband_msg_rx *raw,
38224edb884SFrançois Tigeot 					       struct drm_dp_sideband_msg_reply_body *repmsg)
38324edb884SFrançois Tigeot {
38424edb884SFrançois Tigeot 	int idx = 1;
38524edb884SFrançois Tigeot 	int i;
38624edb884SFrançois Tigeot 	memcpy(repmsg->u.link_addr.guid, &raw->msg[idx], 16);
38724edb884SFrançois Tigeot 	idx += 16;
38824edb884SFrançois Tigeot 	repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
38924edb884SFrançois Tigeot 	idx++;
39024edb884SFrançois Tigeot 	if (idx > raw->curlen)
39124edb884SFrançois Tigeot 		goto fail_len;
39224edb884SFrançois Tigeot 	for (i = 0; i < repmsg->u.link_addr.nports; i++) {
39324edb884SFrançois Tigeot 		if (raw->msg[idx] & 0x80)
39424edb884SFrançois Tigeot 			repmsg->u.link_addr.ports[i].input_port = 1;
39524edb884SFrançois Tigeot 
39624edb884SFrançois Tigeot 		repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
39724edb884SFrançois Tigeot 		repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);
39824edb884SFrançois Tigeot 
39924edb884SFrançois Tigeot 		idx++;
40024edb884SFrançois Tigeot 		if (idx > raw->curlen)
40124edb884SFrançois Tigeot 			goto fail_len;
40224edb884SFrançois Tigeot 		repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
40324edb884SFrançois Tigeot 		repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
40424edb884SFrançois Tigeot 		if (repmsg->u.link_addr.ports[i].input_port == 0)
40524edb884SFrançois Tigeot 			repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
40624edb884SFrançois Tigeot 		idx++;
40724edb884SFrançois Tigeot 		if (idx > raw->curlen)
40824edb884SFrançois Tigeot 			goto fail_len;
40924edb884SFrançois Tigeot 		if (repmsg->u.link_addr.ports[i].input_port == 0) {
41024edb884SFrançois Tigeot 			repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
41124edb884SFrançois Tigeot 			idx++;
41224edb884SFrançois Tigeot 			if (idx > raw->curlen)
41324edb884SFrançois Tigeot 				goto fail_len;
41424edb884SFrançois Tigeot 			memcpy(repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx], 16);
41524edb884SFrançois Tigeot 			idx += 16;
41624edb884SFrançois Tigeot 			if (idx > raw->curlen)
41724edb884SFrançois Tigeot 				goto fail_len;
41824edb884SFrançois Tigeot 			repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
41924edb884SFrançois Tigeot 			repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
42024edb884SFrançois Tigeot 			idx++;
42124edb884SFrançois Tigeot 
42224edb884SFrançois Tigeot 		}
42324edb884SFrançois Tigeot 		if (idx > raw->curlen)
42424edb884SFrançois Tigeot 			goto fail_len;
42524edb884SFrançois Tigeot 	}
42624edb884SFrançois Tigeot 
42724edb884SFrançois Tigeot 	return true;
42824edb884SFrançois Tigeot fail_len:
42924edb884SFrançois Tigeot 	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
43024edb884SFrançois Tigeot 	return false;
43124edb884SFrançois Tigeot }
43224edb884SFrançois Tigeot 
drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)43324edb884SFrançois Tigeot static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
43424edb884SFrançois Tigeot 						   struct drm_dp_sideband_msg_reply_body *repmsg)
43524edb884SFrançois Tigeot {
43624edb884SFrançois Tigeot 	int idx = 1;
43724edb884SFrançois Tigeot 	repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
43824edb884SFrançois Tigeot 	idx++;
43924edb884SFrançois Tigeot 	if (idx > raw->curlen)
44024edb884SFrançois Tigeot 		goto fail_len;
44124edb884SFrançois Tigeot 	repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
44224edb884SFrançois Tigeot 	if (idx > raw->curlen)
44324edb884SFrançois Tigeot 		goto fail_len;
44424edb884SFrançois Tigeot 
44524edb884SFrançois Tigeot 	memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
44624edb884SFrançois Tigeot 	return true;
44724edb884SFrançois Tigeot fail_len:
44824edb884SFrançois Tigeot 	DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
44924edb884SFrançois Tigeot 	return false;
45024edb884SFrançois Tigeot }
45124edb884SFrançois Tigeot 
drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)45224edb884SFrançois Tigeot static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
45324edb884SFrançois Tigeot 						      struct drm_dp_sideband_msg_reply_body *repmsg)
45424edb884SFrançois Tigeot {
45524edb884SFrançois Tigeot 	int idx = 1;
45624edb884SFrançois Tigeot 	repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
45724edb884SFrançois Tigeot 	idx++;
45824edb884SFrançois Tigeot 	if (idx > raw->curlen)
45924edb884SFrançois Tigeot 		goto fail_len;
46024edb884SFrançois Tigeot 	return true;
46124edb884SFrançois Tigeot fail_len:
46224edb884SFrançois Tigeot 	DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
46324edb884SFrançois Tigeot 	return false;
46424edb884SFrançois Tigeot }
46524edb884SFrançois Tigeot 
drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)46624edb884SFrançois Tigeot static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
46724edb884SFrançois Tigeot 						      struct drm_dp_sideband_msg_reply_body *repmsg)
46824edb884SFrançois Tigeot {
46924edb884SFrançois Tigeot 	int idx = 1;
47024edb884SFrançois Tigeot 
47124edb884SFrançois Tigeot 	repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
47224edb884SFrançois Tigeot 	idx++;
47324edb884SFrançois Tigeot 	if (idx > raw->curlen)
47424edb884SFrançois Tigeot 		goto fail_len;
47524edb884SFrançois Tigeot 	repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
47624edb884SFrançois Tigeot 	idx++;
47724edb884SFrançois Tigeot 	/* TODO check */
47824edb884SFrançois Tigeot 	memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
47924edb884SFrançois Tigeot 	return true;
48024edb884SFrançois Tigeot fail_len:
48124edb884SFrançois Tigeot 	DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
48224edb884SFrançois Tigeot 	return false;
48324edb884SFrançois Tigeot }
48424edb884SFrançois Tigeot 
drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)48524edb884SFrançois Tigeot static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
48624edb884SFrançois Tigeot 							  struct drm_dp_sideband_msg_reply_body *repmsg)
48724edb884SFrançois Tigeot {
48824edb884SFrançois Tigeot 	int idx = 1;
48924edb884SFrançois Tigeot 	repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
49024edb884SFrançois Tigeot 	idx++;
49124edb884SFrançois Tigeot 	if (idx > raw->curlen)
49224edb884SFrançois Tigeot 		goto fail_len;
49324edb884SFrançois Tigeot 	repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
49424edb884SFrançois Tigeot 	idx += 2;
49524edb884SFrançois Tigeot 	if (idx > raw->curlen)
49624edb884SFrançois Tigeot 		goto fail_len;
49724edb884SFrançois Tigeot 	repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
49824edb884SFrançois Tigeot 	idx += 2;
49924edb884SFrançois Tigeot 	if (idx > raw->curlen)
50024edb884SFrançois Tigeot 		goto fail_len;
50124edb884SFrançois Tigeot 	return true;
50224edb884SFrançois Tigeot fail_len:
50324edb884SFrançois Tigeot 	DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
50424edb884SFrançois Tigeot 	return false;
50524edb884SFrançois Tigeot }
50624edb884SFrançois Tigeot 
drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)50724edb884SFrançois Tigeot static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
50824edb884SFrançois Tigeot 							  struct drm_dp_sideband_msg_reply_body *repmsg)
50924edb884SFrançois Tigeot {
51024edb884SFrançois Tigeot 	int idx = 1;
51124edb884SFrançois Tigeot 	repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
51224edb884SFrançois Tigeot 	idx++;
51324edb884SFrançois Tigeot 	if (idx > raw->curlen)
51424edb884SFrançois Tigeot 		goto fail_len;
51524edb884SFrançois Tigeot 	repmsg->u.allocate_payload.vcpi = raw->msg[idx];
51624edb884SFrançois Tigeot 	idx++;
51724edb884SFrançois Tigeot 	if (idx > raw->curlen)
51824edb884SFrançois Tigeot 		goto fail_len;
51924edb884SFrançois Tigeot 	repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
52024edb884SFrançois Tigeot 	idx += 2;
52124edb884SFrançois Tigeot 	if (idx > raw->curlen)
52224edb884SFrançois Tigeot 		goto fail_len;
52324edb884SFrançois Tigeot 	return true;
52424edb884SFrançois Tigeot fail_len:
52524edb884SFrançois Tigeot 	DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
52624edb884SFrançois Tigeot 	return false;
52724edb884SFrançois Tigeot }
52824edb884SFrançois Tigeot 
drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)52924edb884SFrançois Tigeot static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
53024edb884SFrançois Tigeot 						    struct drm_dp_sideband_msg_reply_body *repmsg)
53124edb884SFrançois Tigeot {
53224edb884SFrançois Tigeot 	int idx = 1;
53324edb884SFrançois Tigeot 	repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
53424edb884SFrançois Tigeot 	idx++;
53524edb884SFrançois Tigeot 	if (idx > raw->curlen)
53624edb884SFrançois Tigeot 		goto fail_len;
53724edb884SFrançois Tigeot 	repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
53824edb884SFrançois Tigeot 	idx += 2;
53924edb884SFrançois Tigeot 	if (idx > raw->curlen)
54024edb884SFrançois Tigeot 		goto fail_len;
54124edb884SFrançois Tigeot 	return true;
54224edb884SFrançois Tigeot fail_len:
54324edb884SFrançois Tigeot 	DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
54424edb884SFrançois Tigeot 	return false;
54524edb884SFrançois Tigeot }
54624edb884SFrançois Tigeot 
drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * repmsg)547*3f2dd94aSFrançois Tigeot static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
548*3f2dd94aSFrançois Tigeot 						       struct drm_dp_sideband_msg_reply_body *repmsg)
549*3f2dd94aSFrançois Tigeot {
550*3f2dd94aSFrançois Tigeot 	int idx = 1;
551*3f2dd94aSFrançois Tigeot 
552*3f2dd94aSFrançois Tigeot 	repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
553*3f2dd94aSFrançois Tigeot 	idx++;
554*3f2dd94aSFrançois Tigeot 	if (idx > raw->curlen) {
555*3f2dd94aSFrançois Tigeot 		DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
556*3f2dd94aSFrançois Tigeot 			      idx, raw->curlen);
557*3f2dd94aSFrançois Tigeot 		return false;
558*3f2dd94aSFrançois Tigeot 	}
559*3f2dd94aSFrançois Tigeot 	return true;
560*3f2dd94aSFrançois Tigeot }
561*3f2dd94aSFrançois Tigeot 
drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_reply_body * msg)56224edb884SFrançois Tigeot static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
56324edb884SFrançois Tigeot 					struct drm_dp_sideband_msg_reply_body *msg)
56424edb884SFrançois Tigeot {
56524edb884SFrançois Tigeot 	memset(msg, 0, sizeof(*msg));
56624edb884SFrançois Tigeot 	msg->reply_type = (raw->msg[0] & 0x80) >> 7;
56724edb884SFrançois Tigeot 	msg->req_type = (raw->msg[0] & 0x7f);
56824edb884SFrançois Tigeot 
56924edb884SFrançois Tigeot 	if (msg->reply_type) {
57024edb884SFrançois Tigeot 		memcpy(msg->u.nak.guid, &raw->msg[1], 16);
57124edb884SFrançois Tigeot 		msg->u.nak.reason = raw->msg[17];
57224edb884SFrançois Tigeot 		msg->u.nak.nak_data = raw->msg[18];
57324edb884SFrançois Tigeot 		return false;
57424edb884SFrançois Tigeot 	}
57524edb884SFrançois Tigeot 
57624edb884SFrançois Tigeot 	switch (msg->req_type) {
57724edb884SFrançois Tigeot 	case DP_LINK_ADDRESS:
57824edb884SFrançois Tigeot 		return drm_dp_sideband_parse_link_address(raw, msg);
57924edb884SFrançois Tigeot 	case DP_QUERY_PAYLOAD:
58024edb884SFrançois Tigeot 		return drm_dp_sideband_parse_query_payload_ack(raw, msg);
58124edb884SFrançois Tigeot 	case DP_REMOTE_DPCD_READ:
58224edb884SFrançois Tigeot 		return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
58324edb884SFrançois Tigeot 	case DP_REMOTE_DPCD_WRITE:
58424edb884SFrançois Tigeot 		return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
58524edb884SFrançois Tigeot 	case DP_REMOTE_I2C_READ:
58624edb884SFrançois Tigeot 		return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
58724edb884SFrançois Tigeot 	case DP_ENUM_PATH_RESOURCES:
58824edb884SFrançois Tigeot 		return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
58924edb884SFrançois Tigeot 	case DP_ALLOCATE_PAYLOAD:
59024edb884SFrançois Tigeot 		return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
591*3f2dd94aSFrançois Tigeot 	case DP_POWER_DOWN_PHY:
592*3f2dd94aSFrançois Tigeot 	case DP_POWER_UP_PHY:
593*3f2dd94aSFrançois Tigeot 		return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
59424edb884SFrançois Tigeot 	default:
59524edb884SFrançois Tigeot 		DRM_ERROR("Got unknown reply 0x%02x\n", msg->req_type);
59624edb884SFrançois Tigeot 		return false;
59724edb884SFrançois Tigeot 	}
59824edb884SFrançois Tigeot }
59924edb884SFrançois Tigeot 
drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_req_body * msg)60024edb884SFrançois Tigeot static bool drm_dp_sideband_parse_connection_status_notify(struct drm_dp_sideband_msg_rx *raw,
60124edb884SFrançois Tigeot 							   struct drm_dp_sideband_msg_req_body *msg)
60224edb884SFrançois Tigeot {
60324edb884SFrançois Tigeot 	int idx = 1;
60424edb884SFrançois Tigeot 
60524edb884SFrançois Tigeot 	msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
60624edb884SFrançois Tigeot 	idx++;
60724edb884SFrançois Tigeot 	if (idx > raw->curlen)
60824edb884SFrançois Tigeot 		goto fail_len;
60924edb884SFrançois Tigeot 
61024edb884SFrançois Tigeot 	memcpy(msg->u.conn_stat.guid, &raw->msg[idx], 16);
61124edb884SFrançois Tigeot 	idx += 16;
61224edb884SFrançois Tigeot 	if (idx > raw->curlen)
61324edb884SFrançois Tigeot 		goto fail_len;
61424edb884SFrançois Tigeot 
61524edb884SFrançois Tigeot 	msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
61624edb884SFrançois Tigeot 	msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
61724edb884SFrançois Tigeot 	msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
61824edb884SFrançois Tigeot 	msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
61924edb884SFrançois Tigeot 	msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
62024edb884SFrançois Tigeot 	idx++;
62124edb884SFrançois Tigeot 	return true;
62224edb884SFrançois Tigeot fail_len:
62324edb884SFrançois Tigeot 	DRM_DEBUG_KMS("connection status reply parse length fail %d %d\n", idx, raw->curlen);
62424edb884SFrançois Tigeot 	return false;
62524edb884SFrançois Tigeot }
62624edb884SFrançois Tigeot 
drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_req_body * msg)62724edb884SFrançois Tigeot static bool drm_dp_sideband_parse_resource_status_notify(struct drm_dp_sideband_msg_rx *raw,
62824edb884SFrançois Tigeot 							   struct drm_dp_sideband_msg_req_body *msg)
62924edb884SFrançois Tigeot {
63024edb884SFrançois Tigeot 	int idx = 1;
63124edb884SFrançois Tigeot 
63224edb884SFrançois Tigeot 	msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
63324edb884SFrançois Tigeot 	idx++;
63424edb884SFrançois Tigeot 	if (idx > raw->curlen)
63524edb884SFrançois Tigeot 		goto fail_len;
63624edb884SFrançois Tigeot 
63724edb884SFrançois Tigeot 	memcpy(msg->u.resource_stat.guid, &raw->msg[idx], 16);
63824edb884SFrançois Tigeot 	idx += 16;
63924edb884SFrançois Tigeot 	if (idx > raw->curlen)
64024edb884SFrançois Tigeot 		goto fail_len;
64124edb884SFrançois Tigeot 
64224edb884SFrançois Tigeot 	msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
64324edb884SFrançois Tigeot 	idx++;
64424edb884SFrançois Tigeot 	return true;
64524edb884SFrançois Tigeot fail_len:
64624edb884SFrançois Tigeot 	DRM_DEBUG_KMS("resource status reply parse length fail %d %d\n", idx, raw->curlen);
64724edb884SFrançois Tigeot 	return false;
64824edb884SFrançois Tigeot }
64924edb884SFrançois Tigeot 
drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx * raw,struct drm_dp_sideband_msg_req_body * msg)65024edb884SFrançois Tigeot static bool drm_dp_sideband_parse_req(struct drm_dp_sideband_msg_rx *raw,
65124edb884SFrançois Tigeot 				      struct drm_dp_sideband_msg_req_body *msg)
65224edb884SFrançois Tigeot {
65324edb884SFrançois Tigeot 	memset(msg, 0, sizeof(*msg));
65424edb884SFrançois Tigeot 	msg->req_type = (raw->msg[0] & 0x7f);
65524edb884SFrançois Tigeot 
65624edb884SFrançois Tigeot 	switch (msg->req_type) {
65724edb884SFrançois Tigeot 	case DP_CONNECTION_STATUS_NOTIFY:
65824edb884SFrançois Tigeot 		return drm_dp_sideband_parse_connection_status_notify(raw, msg);
65924edb884SFrançois Tigeot 	case DP_RESOURCE_STATUS_NOTIFY:
66024edb884SFrançois Tigeot 		return drm_dp_sideband_parse_resource_status_notify(raw, msg);
66124edb884SFrançois Tigeot 	default:
66224edb884SFrançois Tigeot 		DRM_ERROR("Got unknown request 0x%02x\n", msg->req_type);
66324edb884SFrançois Tigeot 		return false;
66424edb884SFrançois Tigeot 	}
66524edb884SFrançois Tigeot }
66624edb884SFrançois Tigeot 
build_dpcd_write(struct drm_dp_sideband_msg_tx * msg,u8 port_num,u32 offset,u8 num_bytes,u8 * bytes)66724edb884SFrançois Tigeot static int build_dpcd_write(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
66824edb884SFrançois Tigeot {
66924edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
67024edb884SFrançois Tigeot 
67124edb884SFrançois Tigeot 	req.req_type = DP_REMOTE_DPCD_WRITE;
67224edb884SFrançois Tigeot 	req.u.dpcd_write.port_number = port_num;
67324edb884SFrançois Tigeot 	req.u.dpcd_write.dpcd_address = offset;
67424edb884SFrançois Tigeot 	req.u.dpcd_write.num_bytes = num_bytes;
67524edb884SFrançois Tigeot 	req.u.dpcd_write.bytes = bytes;
67624edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
67724edb884SFrançois Tigeot 
67824edb884SFrançois Tigeot 	return 0;
67924edb884SFrançois Tigeot }
68024edb884SFrançois Tigeot 
build_link_address(struct drm_dp_sideband_msg_tx * msg)68124edb884SFrançois Tigeot static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
68224edb884SFrançois Tigeot {
68324edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
68424edb884SFrançois Tigeot 
68524edb884SFrançois Tigeot 	req.req_type = DP_LINK_ADDRESS;
68624edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
68724edb884SFrançois Tigeot 	return 0;
68824edb884SFrançois Tigeot }
68924edb884SFrançois Tigeot 
build_enum_path_resources(struct drm_dp_sideband_msg_tx * msg,int port_num)69024edb884SFrançois Tigeot static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
69124edb884SFrançois Tigeot {
69224edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
69324edb884SFrançois Tigeot 
69424edb884SFrançois Tigeot 	req.req_type = DP_ENUM_PATH_RESOURCES;
69524edb884SFrançois Tigeot 	req.u.port_num.port_number = port_num;
69624edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
69724edb884SFrançois Tigeot 	msg->path_msg = true;
69824edb884SFrançois Tigeot 	return 0;
69924edb884SFrançois Tigeot }
70024edb884SFrançois Tigeot 
build_allocate_payload(struct drm_dp_sideband_msg_tx * msg,int port_num,u8 vcpi,uint16_t pbn,u8 number_sdp_streams,u8 * sdp_stream_sink)70124edb884SFrançois Tigeot static int build_allocate_payload(struct drm_dp_sideband_msg_tx *msg, int port_num,
702aee94f86SFrançois Tigeot 				  u8 vcpi, uint16_t pbn,
703aee94f86SFrançois Tigeot 				  u8 number_sdp_streams,
704aee94f86SFrançois Tigeot 				  u8 *sdp_stream_sink)
70524edb884SFrançois Tigeot {
70624edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
70724edb884SFrançois Tigeot 	memset(&req, 0, sizeof(req));
70824edb884SFrançois Tigeot 	req.req_type = DP_ALLOCATE_PAYLOAD;
70924edb884SFrançois Tigeot 	req.u.allocate_payload.port_number = port_num;
71024edb884SFrançois Tigeot 	req.u.allocate_payload.vcpi = vcpi;
71124edb884SFrançois Tigeot 	req.u.allocate_payload.pbn = pbn;
712aee94f86SFrançois Tigeot 	req.u.allocate_payload.number_sdp_streams = number_sdp_streams;
713aee94f86SFrançois Tigeot 	memcpy(req.u.allocate_payload.sdp_stream_sink, sdp_stream_sink,
714aee94f86SFrançois Tigeot 		   number_sdp_streams);
71524edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
71624edb884SFrançois Tigeot 	msg->path_msg = true;
71724edb884SFrançois Tigeot 	return 0;
71824edb884SFrançois Tigeot }
71924edb884SFrançois Tigeot 
build_power_updown_phy(struct drm_dp_sideband_msg_tx * msg,int port_num,bool power_up)720*3f2dd94aSFrançois Tigeot static int build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
721*3f2dd94aSFrançois Tigeot 				  int port_num, bool power_up)
722*3f2dd94aSFrançois Tigeot {
723*3f2dd94aSFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
724*3f2dd94aSFrançois Tigeot 
725*3f2dd94aSFrançois Tigeot 	if (power_up)
726*3f2dd94aSFrançois Tigeot 		req.req_type = DP_POWER_UP_PHY;
727*3f2dd94aSFrançois Tigeot 	else
728*3f2dd94aSFrançois Tigeot 		req.req_type = DP_POWER_DOWN_PHY;
729*3f2dd94aSFrançois Tigeot 
730*3f2dd94aSFrançois Tigeot 	req.u.port_num.port_number = port_num;
731*3f2dd94aSFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
732*3f2dd94aSFrançois Tigeot 	msg->path_msg = true;
733*3f2dd94aSFrançois Tigeot 	return 0;
734*3f2dd94aSFrançois Tigeot }
735*3f2dd94aSFrançois Tigeot 
drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_vcpi * vcpi)73624edb884SFrançois Tigeot static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr,
73724edb884SFrançois Tigeot 					struct drm_dp_vcpi *vcpi)
73824edb884SFrançois Tigeot {
739aee94f86SFrançois Tigeot 	int ret, vcpi_ret;
74024edb884SFrançois Tigeot 
74124edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
74224edb884SFrançois Tigeot 	ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1);
74324edb884SFrançois Tigeot 	if (ret > mgr->max_payloads) {
74424edb884SFrançois Tigeot 		ret = -EINVAL;
74524edb884SFrançois Tigeot 		DRM_DEBUG_KMS("out of payload ids %d\n", ret);
74624edb884SFrançois Tigeot 		goto out_unlock;
74724edb884SFrançois Tigeot 	}
74824edb884SFrançois Tigeot 
749aee94f86SFrançois Tigeot 	vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1);
750aee94f86SFrançois Tigeot 	if (vcpi_ret > mgr->max_payloads) {
751aee94f86SFrançois Tigeot 		ret = -EINVAL;
752aee94f86SFrançois Tigeot 		DRM_DEBUG_KMS("out of vcpi ids %d\n", ret);
753aee94f86SFrançois Tigeot 		goto out_unlock;
754aee94f86SFrançois Tigeot 	}
755aee94f86SFrançois Tigeot 
75624edb884SFrançois Tigeot 	set_bit(ret, &mgr->payload_mask);
757aee94f86SFrançois Tigeot 	set_bit(vcpi_ret, &mgr->vcpi_mask);
758aee94f86SFrançois Tigeot 	vcpi->vcpi = vcpi_ret + 1;
75924edb884SFrançois Tigeot 	mgr->proposed_vcpis[ret - 1] = vcpi;
76024edb884SFrançois Tigeot out_unlock:
76124edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
76224edb884SFrançois Tigeot 	return ret;
76324edb884SFrançois Tigeot }
76424edb884SFrançois Tigeot 
drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr * mgr,int vcpi)76524edb884SFrançois Tigeot static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr,
766aee94f86SFrançois Tigeot 				      int vcpi)
76724edb884SFrançois Tigeot {
768aee94f86SFrançois Tigeot 	int i;
769aee94f86SFrançois Tigeot 	if (vcpi == 0)
77024edb884SFrançois Tigeot 		return;
77124edb884SFrançois Tigeot 
77224edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
773aee94f86SFrançois Tigeot 	DRM_DEBUG_KMS("putting payload %d\n", vcpi);
774aee94f86SFrançois Tigeot 	clear_bit(vcpi - 1, &mgr->vcpi_mask);
775aee94f86SFrançois Tigeot 
776aee94f86SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
777aee94f86SFrançois Tigeot 		if (mgr->proposed_vcpis[i])
778aee94f86SFrançois Tigeot 			if (mgr->proposed_vcpis[i]->vcpi == vcpi) {
779aee94f86SFrançois Tigeot 				mgr->proposed_vcpis[i] = NULL;
780aee94f86SFrançois Tigeot 				clear_bit(i + 1, &mgr->payload_mask);
781aee94f86SFrançois Tigeot 			}
782aee94f86SFrançois Tigeot 	}
78324edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
78424edb884SFrançois Tigeot }
78524edb884SFrançois Tigeot 
check_txmsg_state(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_sideband_msg_tx * txmsg)78624edb884SFrançois Tigeot static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
78724edb884SFrançois Tigeot 			      struct drm_dp_sideband_msg_tx *txmsg)
78824edb884SFrançois Tigeot {
789*3f2dd94aSFrançois Tigeot 	unsigned int state;
790aee94f86SFrançois Tigeot 
791aee94f86SFrançois Tigeot 	/*
792aee94f86SFrançois Tigeot 	 * All updates to txmsg->state are protected by mgr->qlock, and the two
793aee94f86SFrançois Tigeot 	 * cases we check here are terminal states. For those the barriers
794aee94f86SFrançois Tigeot 	 * provided by the wake_up/wait_event pair are enough.
795aee94f86SFrançois Tigeot 	 */
796*3f2dd94aSFrançois Tigeot 	state = READ_ONCE(txmsg->state);
797*3f2dd94aSFrançois Tigeot 	return (state == DRM_DP_SIDEBAND_TX_RX ||
798*3f2dd94aSFrançois Tigeot 		state == DRM_DP_SIDEBAND_TX_TIMEOUT);
79924edb884SFrançois Tigeot }
80024edb884SFrançois Tigeot 
drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch * mstb,struct drm_dp_sideband_msg_tx * txmsg)80124edb884SFrançois Tigeot static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
80224edb884SFrançois Tigeot 				    struct drm_dp_sideband_msg_tx *txmsg)
80324edb884SFrançois Tigeot {
80424edb884SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
80524edb884SFrançois Tigeot 	int ret;
80624edb884SFrançois Tigeot 
80724edb884SFrançois Tigeot 	ret = wait_event_timeout(mgr->tx_waitq,
80824edb884SFrançois Tigeot 				 check_txmsg_state(mgr, txmsg),
80924edb884SFrançois Tigeot 				 (4 * HZ));
81024edb884SFrançois Tigeot 	mutex_lock(&mstb->mgr->qlock);
81124edb884SFrançois Tigeot 	if (ret > 0) {
81224edb884SFrançois Tigeot 		if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
81324edb884SFrançois Tigeot 			ret = -EIO;
81424edb884SFrançois Tigeot 			goto out;
81524edb884SFrançois Tigeot 		}
81624edb884SFrançois Tigeot 	} else {
81724edb884SFrançois Tigeot 		DRM_DEBUG_KMS("timedout msg send %p %d %d\n", txmsg, txmsg->state, txmsg->seqno);
81824edb884SFrançois Tigeot 
81924edb884SFrançois Tigeot 		/* dump some state */
82024edb884SFrançois Tigeot 		ret = -EIO;
82124edb884SFrançois Tigeot 
82224edb884SFrançois Tigeot 		/* remove from q */
82324edb884SFrançois Tigeot 		if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
82424edb884SFrançois Tigeot 		    txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND) {
82524edb884SFrançois Tigeot 			list_del(&txmsg->next);
82624edb884SFrançois Tigeot 		}
82724edb884SFrançois Tigeot 
82824edb884SFrançois Tigeot 		if (txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
82924edb884SFrançois Tigeot 		    txmsg->state == DRM_DP_SIDEBAND_TX_SENT) {
83024edb884SFrançois Tigeot 			mstb->tx_slots[txmsg->seqno] = NULL;
83124edb884SFrançois Tigeot 		}
83224edb884SFrançois Tigeot 	}
83324edb884SFrançois Tigeot out:
83424edb884SFrançois Tigeot 	mutex_unlock(&mgr->qlock);
83524edb884SFrançois Tigeot 
83624edb884SFrançois Tigeot 	return ret;
83724edb884SFrançois Tigeot }
83824edb884SFrançois Tigeot 
drm_dp_add_mst_branch_device(u8 lct,u8 * rad)83924edb884SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
84024edb884SFrançois Tigeot {
84124edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
84224edb884SFrançois Tigeot 
84324edb884SFrançois Tigeot 	mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
84424edb884SFrançois Tigeot 	if (!mstb)
84524edb884SFrançois Tigeot 		return NULL;
84624edb884SFrançois Tigeot 
84724edb884SFrançois Tigeot 	mstb->lct = lct;
84824edb884SFrançois Tigeot 	if (lct > 1)
84924edb884SFrançois Tigeot 		memcpy(mstb->rad, rad, lct / 2);
85024edb884SFrançois Tigeot 	INIT_LIST_HEAD(&mstb->ports);
85124edb884SFrançois Tigeot 	kref_init(&mstb->kref);
85224edb884SFrançois Tigeot 	return mstb;
85324edb884SFrançois Tigeot }
85424edb884SFrançois Tigeot 
855aee94f86SFrançois Tigeot static void drm_dp_free_mst_port(struct kref *kref);
856aee94f86SFrançois Tigeot 
drm_dp_free_mst_branch_device(struct kref * kref)857aee94f86SFrançois Tigeot static void drm_dp_free_mst_branch_device(struct kref *kref)
858aee94f86SFrançois Tigeot {
859aee94f86SFrançois Tigeot 	struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
860aee94f86SFrançois Tigeot 	if (mstb->port_parent) {
861aee94f86SFrançois Tigeot 		if (list_empty(&mstb->port_parent->next))
862aee94f86SFrançois Tigeot 			kref_put(&mstb->port_parent->kref, drm_dp_free_mst_port);
863aee94f86SFrançois Tigeot 	}
864aee94f86SFrançois Tigeot 	kfree(mstb);
865aee94f86SFrançois Tigeot }
866aee94f86SFrançois Tigeot 
drm_dp_destroy_mst_branch_device(struct kref * kref)86724edb884SFrançois Tigeot static void drm_dp_destroy_mst_branch_device(struct kref *kref)
86824edb884SFrançois Tigeot {
86924edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
87024edb884SFrançois Tigeot 	struct drm_dp_mst_port *port, *tmp;
87124edb884SFrançois Tigeot 	bool wake_tx = false;
87224edb884SFrançois Tigeot 
87324edb884SFrançois Tigeot 	/*
874aee94f86SFrançois Tigeot 	 * init kref again to be used by ports to remove mst branch when it is
875aee94f86SFrançois Tigeot 	 * not needed anymore
876aee94f86SFrançois Tigeot 	 */
877aee94f86SFrançois Tigeot 	kref_init(kref);
878aee94f86SFrançois Tigeot 
879aee94f86SFrançois Tigeot 	if (mstb->port_parent && list_empty(&mstb->port_parent->next))
880aee94f86SFrançois Tigeot 		kref_get(&mstb->port_parent->kref);
881aee94f86SFrançois Tigeot 
882aee94f86SFrançois Tigeot 	/*
88324edb884SFrançois Tigeot 	 * destroy all ports - don't need lock
88424edb884SFrançois Tigeot 	 * as there are no more references to the mst branch
88524edb884SFrançois Tigeot 	 * device at this point.
88624edb884SFrançois Tigeot 	 */
88724edb884SFrançois Tigeot 	list_for_each_entry_safe(port, tmp, &mstb->ports, next) {
88824edb884SFrançois Tigeot 		list_del(&port->next);
88924edb884SFrançois Tigeot 		drm_dp_put_port(port);
89024edb884SFrançois Tigeot 	}
89124edb884SFrançois Tigeot 
89224edb884SFrançois Tigeot 	/* drop any tx slots msg */
89324edb884SFrançois Tigeot 	mutex_lock(&mstb->mgr->qlock);
89424edb884SFrançois Tigeot 	if (mstb->tx_slots[0]) {
89524edb884SFrançois Tigeot 		mstb->tx_slots[0]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
89624edb884SFrançois Tigeot 		mstb->tx_slots[0] = NULL;
89724edb884SFrançois Tigeot 		wake_tx = true;
89824edb884SFrançois Tigeot 	}
89924edb884SFrançois Tigeot 	if (mstb->tx_slots[1]) {
90024edb884SFrançois Tigeot 		mstb->tx_slots[1]->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
90124edb884SFrançois Tigeot 		mstb->tx_slots[1] = NULL;
90224edb884SFrançois Tigeot 		wake_tx = true;
90324edb884SFrançois Tigeot 	}
90424edb884SFrançois Tigeot 	mutex_unlock(&mstb->mgr->qlock);
90524edb884SFrançois Tigeot 
90624edb884SFrançois Tigeot 	if (wake_tx)
907*3f2dd94aSFrançois Tigeot 		wake_up_all(&mstb->mgr->tx_waitq);
908aee94f86SFrançois Tigeot 
909aee94f86SFrançois Tigeot 	kref_put(kref, drm_dp_free_mst_branch_device);
91024edb884SFrançois Tigeot }
91124edb884SFrançois Tigeot 
drm_dp_put_mst_branch_device(struct drm_dp_mst_branch * mstb)91224edb884SFrançois Tigeot static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
91324edb884SFrançois Tigeot {
91424edb884SFrançois Tigeot 	kref_put(&mstb->kref, drm_dp_destroy_mst_branch_device);
91524edb884SFrançois Tigeot }
91624edb884SFrançois Tigeot 
91724edb884SFrançois Tigeot 
drm_dp_port_teardown_pdt(struct drm_dp_mst_port * port,int old_pdt)91824edb884SFrançois Tigeot static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt)
91924edb884SFrançois Tigeot {
9202c9916cdSFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
9212c9916cdSFrançois Tigeot 
92224edb884SFrançois Tigeot 	switch (old_pdt) {
92324edb884SFrançois Tigeot 	case DP_PEER_DEVICE_DP_LEGACY_CONV:
92424edb884SFrançois Tigeot 	case DP_PEER_DEVICE_SST_SINK:
92524edb884SFrançois Tigeot 		/* remove i2c over sideband */
92624edb884SFrançois Tigeot 		drm_dp_mst_unregister_i2c_bus(&port->aux);
92724edb884SFrançois Tigeot 		break;
92824edb884SFrançois Tigeot 	case DP_PEER_DEVICE_MST_BRANCHING:
9292c9916cdSFrançois Tigeot 		mstb = port->mstb;
93024edb884SFrançois Tigeot 		port->mstb = NULL;
9312c9916cdSFrançois Tigeot 		drm_dp_put_mst_branch_device(mstb);
93224edb884SFrançois Tigeot 		break;
93324edb884SFrançois Tigeot 	}
93424edb884SFrançois Tigeot }
93524edb884SFrançois Tigeot 
drm_dp_destroy_port(struct kref * kref)93624edb884SFrançois Tigeot static void drm_dp_destroy_port(struct kref *kref)
93724edb884SFrançois Tigeot {
93824edb884SFrançois Tigeot 	struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
93924edb884SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
940a05eeebfSFrançois Tigeot 
94124edb884SFrançois Tigeot 	if (!port->input) {
94224edb884SFrançois Tigeot 		port->vcpi.num_slots = 0;
9432c9916cdSFrançois Tigeot 
9442c9916cdSFrançois Tigeot 		kfree(port->cached_edid);
94519c468b4SFrançois Tigeot 
946a05eeebfSFrançois Tigeot 		/*
947a05eeebfSFrançois Tigeot 		 * The only time we don't have a connector
948a05eeebfSFrançois Tigeot 		 * on an output port is if the connector init
949a05eeebfSFrançois Tigeot 		 * fails.
950a05eeebfSFrançois Tigeot 		 */
95119c468b4SFrançois Tigeot 		if (port->connector) {
952a05eeebfSFrançois Tigeot 			/* we can't destroy the connector here, as
953a05eeebfSFrançois Tigeot 			 * we might be holding the mode_config.mutex
954a05eeebfSFrançois Tigeot 			 * from an EDID retrieval */
955a05eeebfSFrançois Tigeot 
95619c468b4SFrançois Tigeot 			mutex_lock(&mgr->destroy_connector_lock);
957aee94f86SFrançois Tigeot 			kref_get(&port->parent->kref);
95819c468b4SFrançois Tigeot 			list_add(&port->next, &mgr->destroy_connector_list);
95919c468b4SFrançois Tigeot 			mutex_unlock(&mgr->destroy_connector_lock);
96019c468b4SFrançois Tigeot 			schedule_work(&mgr->destroy_connector_work);
96119c468b4SFrançois Tigeot 			return;
96219c468b4SFrançois Tigeot 		}
963a05eeebfSFrançois Tigeot 		/* no need to clean up vcpi
964a05eeebfSFrançois Tigeot 		 * as if we have no connector we never setup a vcpi */
96524edb884SFrançois Tigeot 		drm_dp_port_teardown_pdt(port, port->pdt);
9661dedbd3bSFrançois Tigeot 		port->pdt = DP_PEER_DEVICE_NONE;
96724edb884SFrançois Tigeot 	}
96824edb884SFrançois Tigeot 	kfree(port);
96924edb884SFrançois Tigeot }
97024edb884SFrançois Tigeot 
drm_dp_put_port(struct drm_dp_mst_port * port)97124edb884SFrançois Tigeot static void drm_dp_put_port(struct drm_dp_mst_port *port)
97224edb884SFrançois Tigeot {
97324edb884SFrançois Tigeot 	kref_put(&port->kref, drm_dp_destroy_port);
97424edb884SFrançois Tigeot }
97524edb884SFrançois Tigeot 
drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch * mstb,struct drm_dp_mst_branch * to_find)97624edb884SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_mst_get_validated_mstb_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_branch *to_find)
97724edb884SFrançois Tigeot {
97824edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
97924edb884SFrançois Tigeot 	struct drm_dp_mst_branch *rmstb;
98024edb884SFrançois Tigeot 	if (to_find == mstb) {
98124edb884SFrançois Tigeot 		kref_get(&mstb->kref);
98224edb884SFrançois Tigeot 		return mstb;
98324edb884SFrançois Tigeot 	}
98424edb884SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
98524edb884SFrançois Tigeot 		if (port->mstb) {
98624edb884SFrançois Tigeot 			rmstb = drm_dp_mst_get_validated_mstb_ref_locked(port->mstb, to_find);
98724edb884SFrançois Tigeot 			if (rmstb)
98824edb884SFrançois Tigeot 				return rmstb;
98924edb884SFrançois Tigeot 		}
99024edb884SFrançois Tigeot 	}
99124edb884SFrançois Tigeot 	return NULL;
99224edb884SFrançois Tigeot }
99324edb884SFrançois Tigeot 
drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb)99424edb884SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_get_validated_mstb_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_branch *mstb)
99524edb884SFrançois Tigeot {
99624edb884SFrançois Tigeot 	struct drm_dp_mst_branch *rmstb = NULL;
99724edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
99824edb884SFrançois Tigeot 	if (mgr->mst_primary)
99924edb884SFrançois Tigeot 		rmstb = drm_dp_mst_get_validated_mstb_ref_locked(mgr->mst_primary, mstb);
100024edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
100124edb884SFrançois Tigeot 	return rmstb;
100224edb884SFrançois Tigeot }
100324edb884SFrançois Tigeot 
drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch * mstb,struct drm_dp_mst_port * to_find)100424edb884SFrançois Tigeot static struct drm_dp_mst_port *drm_dp_mst_get_port_ref_locked(struct drm_dp_mst_branch *mstb, struct drm_dp_mst_port *to_find)
100524edb884SFrançois Tigeot {
100624edb884SFrançois Tigeot 	struct drm_dp_mst_port *port, *mport;
100724edb884SFrançois Tigeot 
100824edb884SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
100924edb884SFrançois Tigeot 		if (port == to_find) {
101024edb884SFrançois Tigeot 			kref_get(&port->kref);
101124edb884SFrançois Tigeot 			return port;
101224edb884SFrançois Tigeot 		}
101324edb884SFrançois Tigeot 		if (port->mstb) {
101424edb884SFrançois Tigeot 			mport = drm_dp_mst_get_port_ref_locked(port->mstb, to_find);
101524edb884SFrançois Tigeot 			if (mport)
101624edb884SFrançois Tigeot 				return mport;
101724edb884SFrançois Tigeot 		}
101824edb884SFrançois Tigeot 	}
101924edb884SFrançois Tigeot 	return NULL;
102024edb884SFrançois Tigeot }
102124edb884SFrançois Tigeot 
drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)102224edb884SFrançois Tigeot static struct drm_dp_mst_port *drm_dp_get_validated_port_ref(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
102324edb884SFrançois Tigeot {
102424edb884SFrançois Tigeot 	struct drm_dp_mst_port *rport = NULL;
102524edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
102624edb884SFrançois Tigeot 	if (mgr->mst_primary)
102724edb884SFrançois Tigeot 		rport = drm_dp_mst_get_port_ref_locked(mgr->mst_primary, port);
102824edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
102924edb884SFrançois Tigeot 	return rport;
103024edb884SFrançois Tigeot }
103124edb884SFrançois Tigeot 
drm_dp_get_port(struct drm_dp_mst_branch * mstb,u8 port_num)103224edb884SFrançois Tigeot static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
103324edb884SFrançois Tigeot {
103424edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
103524edb884SFrançois Tigeot 
103624edb884SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
103724edb884SFrançois Tigeot 		if (port->port_num == port_num) {
103824edb884SFrançois Tigeot 			kref_get(&port->kref);
103924edb884SFrançois Tigeot 			return port;
104024edb884SFrançois Tigeot 		}
104124edb884SFrançois Tigeot 	}
104224edb884SFrançois Tigeot 
104324edb884SFrançois Tigeot 	return NULL;
104424edb884SFrançois Tigeot }
104524edb884SFrançois Tigeot 
104624edb884SFrançois Tigeot /*
104724edb884SFrançois Tigeot  * calculate a new RAD for this MST branch device
104824edb884SFrançois Tigeot  * if parent has an LCT of 2 then it has 1 nibble of RAD,
104924edb884SFrançois Tigeot  * if parent has an LCT of 3 then it has 2 nibbles of RAD,
105024edb884SFrançois Tigeot  */
drm_dp_calculate_rad(struct drm_dp_mst_port * port,u8 * rad)105124edb884SFrançois Tigeot static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
105224edb884SFrançois Tigeot 				 u8 *rad)
105324edb884SFrançois Tigeot {
1054aee94f86SFrançois Tigeot 	int parent_lct = port->parent->lct;
105524edb884SFrançois Tigeot 	int shift = 4;
1056aee94f86SFrançois Tigeot 	int idx = (parent_lct - 1) / 2;
1057aee94f86SFrançois Tigeot 	if (parent_lct > 1) {
1058aee94f86SFrançois Tigeot 		memcpy(rad, port->parent->rad, idx + 1);
1059aee94f86SFrançois Tigeot 		shift = (parent_lct % 2) ? 4 : 0;
106024edb884SFrançois Tigeot 	} else
106124edb884SFrançois Tigeot 		rad[0] = 0;
106224edb884SFrançois Tigeot 
106324edb884SFrançois Tigeot 	rad[idx] |= port->port_num << shift;
1064aee94f86SFrançois Tigeot 	return parent_lct + 1;
106524edb884SFrançois Tigeot }
106624edb884SFrançois Tigeot 
106724edb884SFrançois Tigeot /*
106824edb884SFrançois Tigeot  * return sends link address for new mstb
106924edb884SFrançois Tigeot  */
drm_dp_port_setup_pdt(struct drm_dp_mst_port * port)107024edb884SFrançois Tigeot static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
107124edb884SFrançois Tigeot {
107224edb884SFrançois Tigeot 	int ret;
107324edb884SFrançois Tigeot 	u8 rad[6], lct;
107424edb884SFrançois Tigeot 	bool send_link = false;
107524edb884SFrançois Tigeot 	switch (port->pdt) {
107624edb884SFrançois Tigeot 	case DP_PEER_DEVICE_DP_LEGACY_CONV:
107724edb884SFrançois Tigeot 	case DP_PEER_DEVICE_SST_SINK:
107824edb884SFrançois Tigeot 		/* add i2c over sideband */
107924edb884SFrançois Tigeot 		ret = drm_dp_mst_register_i2c_bus(&port->aux);
108024edb884SFrançois Tigeot 		break;
108124edb884SFrançois Tigeot 	case DP_PEER_DEVICE_MST_BRANCHING:
108224edb884SFrançois Tigeot 		lct = drm_dp_calculate_rad(port, rad);
108324edb884SFrançois Tigeot 
108424edb884SFrançois Tigeot 		port->mstb = drm_dp_add_mst_branch_device(lct, rad);
108524edb884SFrançois Tigeot 		port->mstb->mgr = port->mgr;
108624edb884SFrançois Tigeot 		port->mstb->port_parent = port;
108724edb884SFrançois Tigeot 
108824edb884SFrançois Tigeot 		send_link = true;
108924edb884SFrançois Tigeot 		break;
109024edb884SFrançois Tigeot 	}
109124edb884SFrançois Tigeot 	return send_link;
109224edb884SFrançois Tigeot }
109324edb884SFrançois Tigeot 
drm_dp_check_mstb_guid(struct drm_dp_mst_branch * mstb,u8 * guid)1094aee94f86SFrançois Tigeot static void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
109524edb884SFrançois Tigeot {
109624edb884SFrançois Tigeot 	int ret;
1097aee94f86SFrançois Tigeot 
1098aee94f86SFrançois Tigeot 	memcpy(mstb->guid, guid, 16);
1099aee94f86SFrançois Tigeot 
1100aee94f86SFrançois Tigeot 	if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
1101aee94f86SFrançois Tigeot 		if (mstb->port_parent) {
1102aee94f86SFrançois Tigeot 			ret = drm_dp_send_dpcd_write(
1103aee94f86SFrançois Tigeot 					mstb->mgr,
1104aee94f86SFrançois Tigeot 					mstb->port_parent,
110524edb884SFrançois Tigeot 					DP_GUID,
1106aee94f86SFrançois Tigeot 					16,
1107aee94f86SFrançois Tigeot 					mstb->guid);
1108aee94f86SFrançois Tigeot 		} else {
1109aee94f86SFrançois Tigeot 
1110aee94f86SFrançois Tigeot 			ret = drm_dp_dpcd_write(
1111aee94f86SFrançois Tigeot 					mstb->mgr->aux,
1112aee94f86SFrançois Tigeot 					DP_GUID,
1113aee94f86SFrançois Tigeot 					mstb->guid,
1114aee94f86SFrançois Tigeot 					16);
111524edb884SFrançois Tigeot 		}
111624edb884SFrançois Tigeot 	}
111724edb884SFrançois Tigeot }
111824edb884SFrançois Tigeot 
build_mst_prop_path(const struct drm_dp_mst_branch * mstb,int pnum,char * proppath,size_t proppath_size)1119a05eeebfSFrançois Tigeot static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb,
1120a05eeebfSFrançois Tigeot 				int pnum,
11212c9916cdSFrançois Tigeot 				char *proppath,
11222c9916cdSFrançois Tigeot 				size_t proppath_size)
112324edb884SFrançois Tigeot {
112424edb884SFrançois Tigeot 	int i;
112524edb884SFrançois Tigeot 	char temp[8];
1126*3f2dd94aSFrançois Tigeot 	snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id);
112724edb884SFrançois Tigeot 	for (i = 0; i < (mstb->lct - 1); i++) {
112824edb884SFrançois Tigeot 		int shift = (i % 2) ? 0 : 4;
1129aee94f86SFrançois Tigeot 		int port_num = (mstb->rad[i / 2] >> shift) & 0xf;
1130*3f2dd94aSFrançois Tigeot 		snprintf(temp, sizeof(temp), "-%d", port_num);
11312c9916cdSFrançois Tigeot 		strlcat(proppath, temp, proppath_size);
113224edb884SFrançois Tigeot 	}
1133*3f2dd94aSFrançois Tigeot 	snprintf(temp, sizeof(temp), "-%d", pnum);
11342c9916cdSFrançois Tigeot 	strlcat(proppath, temp, proppath_size);
113524edb884SFrançois Tigeot }
113624edb884SFrançois Tigeot 
drm_dp_add_port(struct drm_dp_mst_branch * mstb,struct drm_device * dev,struct drm_dp_link_addr_reply_port * port_msg)113724edb884SFrançois Tigeot static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
1138a85cb24fSFrançois Tigeot 			    struct drm_device *dev,
113924edb884SFrançois Tigeot 			    struct drm_dp_link_addr_reply_port *port_msg)
114024edb884SFrançois Tigeot {
114124edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
114224edb884SFrançois Tigeot 	bool ret;
114324edb884SFrançois Tigeot 	bool created = false;
114424edb884SFrançois Tigeot 	int old_pdt = 0;
114524edb884SFrançois Tigeot 	int old_ddps = 0;
114624edb884SFrançois Tigeot 	port = drm_dp_get_port(mstb, port_msg->port_number);
114724edb884SFrançois Tigeot 	if (!port) {
114824edb884SFrançois Tigeot 		port = kzalloc(sizeof(*port), GFP_KERNEL);
114924edb884SFrançois Tigeot 		if (!port)
115024edb884SFrançois Tigeot 			return;
115124edb884SFrançois Tigeot 		kref_init(&port->kref);
115224edb884SFrançois Tigeot 		port->parent = mstb;
115324edb884SFrançois Tigeot 		port->port_num = port_msg->port_number;
115424edb884SFrançois Tigeot 		port->mgr = mstb->mgr;
115524edb884SFrançois Tigeot 		port->aux.name = "DPMST";
1156a85cb24fSFrançois Tigeot 		port->aux.dev = dev->dev;
115724edb884SFrançois Tigeot 		created = true;
115824edb884SFrançois Tigeot 	} else {
115924edb884SFrançois Tigeot 		old_pdt = port->pdt;
116024edb884SFrançois Tigeot 		old_ddps = port->ddps;
116124edb884SFrançois Tigeot 	}
116224edb884SFrançois Tigeot 
116324edb884SFrançois Tigeot 	port->pdt = port_msg->peer_device_type;
116424edb884SFrançois Tigeot 	port->input = port_msg->input_port;
116524edb884SFrançois Tigeot 	port->mcs = port_msg->mcs;
116624edb884SFrançois Tigeot 	port->ddps = port_msg->ddps;
116724edb884SFrançois Tigeot 	port->ldps = port_msg->legacy_device_plug_status;
116824edb884SFrançois Tigeot 	port->dpcd_rev = port_msg->dpcd_revision;
116924edb884SFrançois Tigeot 	port->num_sdp_streams = port_msg->num_sdp_streams;
117024edb884SFrançois Tigeot 	port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
117124edb884SFrançois Tigeot 
117224edb884SFrançois Tigeot 	/* manage mstb port lists with mgr lock - take a reference
117324edb884SFrançois Tigeot 	   for this list */
117424edb884SFrançois Tigeot 	if (created) {
117524edb884SFrançois Tigeot 		mutex_lock(&mstb->mgr->lock);
117624edb884SFrançois Tigeot 		kref_get(&port->kref);
117724edb884SFrançois Tigeot 		list_add(&port->next, &mstb->ports);
117824edb884SFrançois Tigeot 		mutex_unlock(&mstb->mgr->lock);
117924edb884SFrançois Tigeot 	}
118024edb884SFrançois Tigeot 
118124edb884SFrançois Tigeot 	if (old_ddps != port->ddps) {
118224edb884SFrançois Tigeot 		if (port->ddps) {
118324edb884SFrançois Tigeot 			if (!port->input)
118424edb884SFrançois Tigeot 				drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
118524edb884SFrançois Tigeot 		} else {
118624edb884SFrançois Tigeot 			port->available_pbn = 0;
118724edb884SFrançois Tigeot 			}
118824edb884SFrançois Tigeot 	}
118924edb884SFrançois Tigeot 
119024edb884SFrançois Tigeot 	if (old_pdt != port->pdt && !port->input) {
119124edb884SFrançois Tigeot 		drm_dp_port_teardown_pdt(port, old_pdt);
119224edb884SFrançois Tigeot 
119324edb884SFrançois Tigeot 		ret = drm_dp_port_setup_pdt(port);
1194a05eeebfSFrançois Tigeot 		if (ret == true)
119524edb884SFrançois Tigeot 			drm_dp_send_link_address(mstb->mgr, port->mstb);
119624edb884SFrançois Tigeot 	}
119724edb884SFrançois Tigeot 
119824edb884SFrançois Tigeot 	if (created && !port->input) {
119924edb884SFrançois Tigeot 		char proppath[255];
1200a05eeebfSFrançois Tigeot 
1201a05eeebfSFrançois Tigeot 		build_mst_prop_path(mstb, port->port_num, proppath, sizeof(proppath));
120224edb884SFrançois Tigeot 		port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath);
1203a05eeebfSFrançois Tigeot 		if (!port->connector) {
1204a05eeebfSFrançois Tigeot 			/* remove it from the port list */
1205a05eeebfSFrançois Tigeot 			mutex_lock(&mstb->mgr->lock);
1206a05eeebfSFrançois Tigeot 			list_del(&port->next);
1207a05eeebfSFrançois Tigeot 			mutex_unlock(&mstb->mgr->lock);
1208a05eeebfSFrançois Tigeot 			/* drop port list reference */
1209a05eeebfSFrançois Tigeot 			drm_dp_put_port(port);
1210a05eeebfSFrançois Tigeot 			goto out;
1211a05eeebfSFrançois Tigeot 		}
12121dedbd3bSFrançois Tigeot 		if ((port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV ||
12131dedbd3bSFrançois Tigeot 		     port->pdt == DP_PEER_DEVICE_SST_SINK) &&
12141dedbd3bSFrançois Tigeot 		    port->port_num >= DP_MST_LOGICAL_PORT_0) {
12152c9916cdSFrançois Tigeot 			port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
1216a05eeebfSFrançois Tigeot 			drm_mode_connector_set_tile_property(port->connector);
12172c9916cdSFrançois Tigeot 		}
1218a05eeebfSFrançois Tigeot 		(*mstb->mgr->cbs->register_connector)(port->connector);
121924edb884SFrançois Tigeot 	}
122024edb884SFrançois Tigeot 
1221a05eeebfSFrançois Tigeot out:
122224edb884SFrançois Tigeot 	/* put reference to this port */
122324edb884SFrançois Tigeot 	drm_dp_put_port(port);
122424edb884SFrançois Tigeot }
122524edb884SFrançois Tigeot 
drm_dp_update_port(struct drm_dp_mst_branch * mstb,struct drm_dp_connection_status_notify * conn_stat)122624edb884SFrançois Tigeot static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
122724edb884SFrançois Tigeot 			       struct drm_dp_connection_status_notify *conn_stat)
122824edb884SFrançois Tigeot {
122924edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
123024edb884SFrançois Tigeot 	int old_pdt;
123124edb884SFrançois Tigeot 	int old_ddps;
123224edb884SFrançois Tigeot 	bool dowork = false;
123324edb884SFrançois Tigeot 	port = drm_dp_get_port(mstb, conn_stat->port_number);
123424edb884SFrançois Tigeot 	if (!port)
123524edb884SFrançois Tigeot 		return;
123624edb884SFrançois Tigeot 
123724edb884SFrançois Tigeot 	old_ddps = port->ddps;
123824edb884SFrançois Tigeot 	old_pdt = port->pdt;
123924edb884SFrançois Tigeot 	port->pdt = conn_stat->peer_device_type;
124024edb884SFrançois Tigeot 	port->mcs = conn_stat->message_capability_status;
124124edb884SFrançois Tigeot 	port->ldps = conn_stat->legacy_device_plug_status;
124224edb884SFrançois Tigeot 	port->ddps = conn_stat->displayport_device_plug_status;
124324edb884SFrançois Tigeot 
124424edb884SFrançois Tigeot 	if (old_ddps != port->ddps) {
124524edb884SFrançois Tigeot 		if (port->ddps) {
124624edb884SFrançois Tigeot 			dowork = true;
124724edb884SFrançois Tigeot 		} else {
124824edb884SFrançois Tigeot 			port->available_pbn = 0;
124924edb884SFrançois Tigeot 		}
125024edb884SFrançois Tigeot 	}
125124edb884SFrançois Tigeot 	if (old_pdt != port->pdt && !port->input) {
125224edb884SFrançois Tigeot 		drm_dp_port_teardown_pdt(port, old_pdt);
125324edb884SFrançois Tigeot 
125424edb884SFrançois Tigeot 		if (drm_dp_port_setup_pdt(port))
125524edb884SFrançois Tigeot 			dowork = true;
125624edb884SFrançois Tigeot 	}
125724edb884SFrançois Tigeot 
125824edb884SFrançois Tigeot 	drm_dp_put_port(port);
125924edb884SFrançois Tigeot 	if (dowork)
126024edb884SFrançois Tigeot 		queue_work(system_long_wq, &mstb->mgr->work);
126124edb884SFrançois Tigeot 
126224edb884SFrançois Tigeot }
126324edb884SFrançois Tigeot 
drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr * mgr,u8 lct,u8 * rad)126424edb884SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device(struct drm_dp_mst_topology_mgr *mgr,
126524edb884SFrançois Tigeot 							       u8 lct, u8 *rad)
126624edb884SFrançois Tigeot {
126724edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
126824edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
126924edb884SFrançois Tigeot 	int i;
127024edb884SFrançois Tigeot 	/* find the port by iterating down */
127119c468b4SFrançois Tigeot 
127219c468b4SFrançois Tigeot 	mutex_lock(&mgr->lock);
127324edb884SFrançois Tigeot 	mstb = mgr->mst_primary;
127424edb884SFrançois Tigeot 
127524edb884SFrançois Tigeot 	for (i = 0; i < lct - 1; i++) {
127624edb884SFrançois Tigeot 		int shift = (i % 2) ? 0 : 4;
1277aee94f86SFrançois Tigeot 		int port_num = (rad[i / 2] >> shift) & 0xf;
127824edb884SFrançois Tigeot 
127924edb884SFrançois Tigeot 		list_for_each_entry(port, &mstb->ports, next) {
128024edb884SFrançois Tigeot 			if (port->port_num == port_num) {
1281a05eeebfSFrançois Tigeot 				mstb = port->mstb;
1282a05eeebfSFrançois Tigeot 				if (!mstb) {
128324edb884SFrançois Tigeot 					DRM_ERROR("failed to lookup MSTB with lct %d, rad %02x\n", lct, rad[0]);
1284a05eeebfSFrançois Tigeot 					goto out;
128524edb884SFrançois Tigeot 				}
128624edb884SFrançois Tigeot 
128724edb884SFrançois Tigeot 				break;
128824edb884SFrançois Tigeot 			}
128924edb884SFrançois Tigeot 		}
129024edb884SFrançois Tigeot 	}
129124edb884SFrançois Tigeot 	kref_get(&mstb->kref);
1292a05eeebfSFrançois Tigeot out:
129319c468b4SFrançois Tigeot 	mutex_unlock(&mgr->lock);
129424edb884SFrançois Tigeot 	return mstb;
129524edb884SFrançois Tigeot }
129624edb884SFrançois Tigeot 
get_mst_branch_device_by_guid_helper(struct drm_dp_mst_branch * mstb,uint8_t * guid)1297aee94f86SFrançois Tigeot static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
1298aee94f86SFrançois Tigeot 	struct drm_dp_mst_branch *mstb,
1299aee94f86SFrançois Tigeot 	uint8_t *guid)
1300aee94f86SFrançois Tigeot {
1301aee94f86SFrançois Tigeot 	struct drm_dp_mst_branch *found_mstb;
1302aee94f86SFrançois Tigeot 	struct drm_dp_mst_port *port;
1303aee94f86SFrançois Tigeot 
1304aee94f86SFrançois Tigeot 	if (memcmp(mstb->guid, guid, 16) == 0)
1305aee94f86SFrançois Tigeot 		return mstb;
1306aee94f86SFrançois Tigeot 
1307aee94f86SFrançois Tigeot 
1308aee94f86SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
1309aee94f86SFrançois Tigeot 		if (!port->mstb)
1310aee94f86SFrançois Tigeot 			continue;
1311aee94f86SFrançois Tigeot 
1312aee94f86SFrançois Tigeot 		found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
1313aee94f86SFrançois Tigeot 
1314aee94f86SFrançois Tigeot 		if (found_mstb)
1315aee94f86SFrançois Tigeot 			return found_mstb;
1316aee94f86SFrançois Tigeot 	}
1317aee94f86SFrançois Tigeot 
1318aee94f86SFrançois Tigeot 	return NULL;
1319aee94f86SFrançois Tigeot }
1320aee94f86SFrançois Tigeot 
drm_dp_get_mst_branch_device_by_guid(struct drm_dp_mst_topology_mgr * mgr,uint8_t * guid)1321aee94f86SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device_by_guid(
1322aee94f86SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr,
1323aee94f86SFrançois Tigeot 	uint8_t *guid)
1324aee94f86SFrançois Tigeot {
1325aee94f86SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
1326aee94f86SFrançois Tigeot 
1327aee94f86SFrançois Tigeot 	/* find the port by iterating down */
1328aee94f86SFrançois Tigeot 	mutex_lock(&mgr->lock);
1329aee94f86SFrançois Tigeot 
1330aee94f86SFrançois Tigeot 	mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
1331aee94f86SFrançois Tigeot 
1332aee94f86SFrançois Tigeot 	if (mstb)
1333aee94f86SFrançois Tigeot 		kref_get(&mstb->kref);
1334aee94f86SFrançois Tigeot 
1335aee94f86SFrançois Tigeot 	mutex_unlock(&mgr->lock);
1336aee94f86SFrançois Tigeot 	return mstb;
1337aee94f86SFrançois Tigeot }
1338aee94f86SFrançois Tigeot 
drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb)133924edb884SFrançois Tigeot static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
134024edb884SFrançois Tigeot 					       struct drm_dp_mst_branch *mstb)
134124edb884SFrançois Tigeot {
134224edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
134319c468b4SFrançois Tigeot 	struct drm_dp_mst_branch *mstb_child;
1344a05eeebfSFrançois Tigeot 	if (!mstb->link_address_sent)
134524edb884SFrançois Tigeot 		drm_dp_send_link_address(mgr, mstb);
1346a05eeebfSFrançois Tigeot 
134724edb884SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
134824edb884SFrançois Tigeot 		if (port->input)
134924edb884SFrançois Tigeot 			continue;
135024edb884SFrançois Tigeot 
135124edb884SFrançois Tigeot 		if (!port->ddps)
135224edb884SFrançois Tigeot 			continue;
135324edb884SFrançois Tigeot 
135424edb884SFrançois Tigeot 		if (!port->available_pbn)
135524edb884SFrançois Tigeot 			drm_dp_send_enum_path_resources(mgr, mstb, port);
135624edb884SFrançois Tigeot 
135719c468b4SFrançois Tigeot 		if (port->mstb) {
135819c468b4SFrançois Tigeot 			mstb_child = drm_dp_get_validated_mstb_ref(mgr, port->mstb);
135919c468b4SFrançois Tigeot 			if (mstb_child) {
136019c468b4SFrançois Tigeot 				drm_dp_check_and_send_link_address(mgr, mstb_child);
136119c468b4SFrançois Tigeot 				drm_dp_put_mst_branch_device(mstb_child);
136219c468b4SFrançois Tigeot 			}
136319c468b4SFrançois Tigeot 		}
136424edb884SFrançois Tigeot 	}
136524edb884SFrançois Tigeot }
136624edb884SFrançois Tigeot 
drm_dp_mst_link_probe_work(struct work_struct * work)136724edb884SFrançois Tigeot static void drm_dp_mst_link_probe_work(struct work_struct *work)
136824edb884SFrançois Tigeot {
136924edb884SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, work);
137019c468b4SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
137124edb884SFrançois Tigeot 
137219c468b4SFrançois Tigeot 	mutex_lock(&mgr->lock);
137319c468b4SFrançois Tigeot 	mstb = mgr->mst_primary;
137419c468b4SFrançois Tigeot 	if (mstb) {
137519c468b4SFrançois Tigeot 		kref_get(&mstb->kref);
137619c468b4SFrançois Tigeot 	}
137719c468b4SFrançois Tigeot 	mutex_unlock(&mgr->lock);
137819c468b4SFrançois Tigeot 	if (mstb) {
137919c468b4SFrançois Tigeot 		drm_dp_check_and_send_link_address(mgr, mstb);
138019c468b4SFrançois Tigeot 		drm_dp_put_mst_branch_device(mstb);
138119c468b4SFrançois Tigeot 	}
138224edb884SFrançois Tigeot }
138324edb884SFrançois Tigeot 
drm_dp_validate_guid(struct drm_dp_mst_topology_mgr * mgr,u8 * guid)138424edb884SFrançois Tigeot static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
138524edb884SFrançois Tigeot 				 u8 *guid)
138624edb884SFrançois Tigeot {
1387*3f2dd94aSFrançois Tigeot 	u64 salt;
138824edb884SFrançois Tigeot 
1389*3f2dd94aSFrançois Tigeot 	if (memchr_inv(guid, 0, 16))
1390*3f2dd94aSFrançois Tigeot 		return true;
1391*3f2dd94aSFrançois Tigeot 
1392*3f2dd94aSFrançois Tigeot 	salt = get_jiffies_64();
1393*3f2dd94aSFrançois Tigeot 
139424edb884SFrançois Tigeot 	memcpy(&guid[0], &salt, sizeof(u64));
139524edb884SFrançois Tigeot 	memcpy(&guid[8], &salt, sizeof(u64));
1396*3f2dd94aSFrançois Tigeot 
139724edb884SFrançois Tigeot 	return false;
139824edb884SFrançois Tigeot }
139924edb884SFrançois Tigeot 
140024edb884SFrançois Tigeot #if 0
140124edb884SFrançois Tigeot static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
140224edb884SFrançois Tigeot {
140324edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body req;
140424edb884SFrançois Tigeot 
140524edb884SFrançois Tigeot 	req.req_type = DP_REMOTE_DPCD_READ;
140624edb884SFrançois Tigeot 	req.u.dpcd_read.port_number = port_num;
140724edb884SFrançois Tigeot 	req.u.dpcd_read.dpcd_address = offset;
140824edb884SFrançois Tigeot 	req.u.dpcd_read.num_bytes = num_bytes;
140924edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&req, msg);
141024edb884SFrançois Tigeot 
141124edb884SFrançois Tigeot 	return 0;
141224edb884SFrançois Tigeot }
141324edb884SFrançois Tigeot #endif
141424edb884SFrançois Tigeot 
drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr * mgr,bool up,u8 * msg,int len)141524edb884SFrançois Tigeot static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
141624edb884SFrançois Tigeot 				    bool up, u8 *msg, int len)
141724edb884SFrançois Tigeot {
141824edb884SFrançois Tigeot 	int ret;
141924edb884SFrançois Tigeot 	int regbase = up ? DP_SIDEBAND_MSG_UP_REP_BASE : DP_SIDEBAND_MSG_DOWN_REQ_BASE;
142024edb884SFrançois Tigeot 	int tosend, total, offset;
142124edb884SFrançois Tigeot 	int retries = 0;
142224edb884SFrançois Tigeot 
142324edb884SFrançois Tigeot retry:
142424edb884SFrançois Tigeot 	total = len;
142524edb884SFrançois Tigeot 	offset = 0;
142624edb884SFrançois Tigeot 	do {
142724edb884SFrançois Tigeot 		tosend = min3(mgr->max_dpcd_transaction_bytes, 16, total);
142824edb884SFrançois Tigeot 
142924edb884SFrançois Tigeot 		ret = drm_dp_dpcd_write(mgr->aux, regbase + offset,
143024edb884SFrançois Tigeot 					&msg[offset],
143124edb884SFrançois Tigeot 					tosend);
143224edb884SFrançois Tigeot 		if (ret != tosend) {
143324edb884SFrançois Tigeot 			if (ret == -EIO && retries < 5) {
143424edb884SFrançois Tigeot 				retries++;
143524edb884SFrançois Tigeot 				goto retry;
143624edb884SFrançois Tigeot 			}
143724edb884SFrançois Tigeot 			DRM_DEBUG_KMS("failed to dpcd write %d %d\n", tosend, ret);
143824edb884SFrançois Tigeot 
143924edb884SFrançois Tigeot 			return -EIO;
144024edb884SFrançois Tigeot 		}
144124edb884SFrançois Tigeot 		offset += tosend;
144224edb884SFrançois Tigeot 		total -= tosend;
144324edb884SFrançois Tigeot 	} while (total > 0);
144424edb884SFrançois Tigeot 	return 0;
144524edb884SFrançois Tigeot }
144624edb884SFrançois Tigeot 
set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr * hdr,struct drm_dp_sideband_msg_tx * txmsg)144724edb884SFrançois Tigeot static int set_hdr_from_dst_qlock(struct drm_dp_sideband_msg_hdr *hdr,
144824edb884SFrançois Tigeot 				  struct drm_dp_sideband_msg_tx *txmsg)
144924edb884SFrançois Tigeot {
145024edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb = txmsg->dst;
1451aee94f86SFrançois Tigeot 	u8 req_type;
145224edb884SFrançois Tigeot 
145324edb884SFrançois Tigeot 	/* both msg slots are full */
145424edb884SFrançois Tigeot 	if (txmsg->seqno == -1) {
145524edb884SFrançois Tigeot 		if (mstb->tx_slots[0] && mstb->tx_slots[1]) {
145624edb884SFrançois Tigeot 			DRM_DEBUG_KMS("%s: failed to find slot\n", __func__);
145724edb884SFrançois Tigeot 			return -EAGAIN;
145824edb884SFrançois Tigeot 		}
145924edb884SFrançois Tigeot 		if (mstb->tx_slots[0] == NULL && mstb->tx_slots[1] == NULL) {
146024edb884SFrançois Tigeot 			txmsg->seqno = mstb->last_seqno;
146124edb884SFrançois Tigeot 			mstb->last_seqno ^= 1;
146224edb884SFrançois Tigeot 		} else if (mstb->tx_slots[0] == NULL)
146324edb884SFrançois Tigeot 			txmsg->seqno = 0;
146424edb884SFrançois Tigeot 		else
146524edb884SFrançois Tigeot 			txmsg->seqno = 1;
146624edb884SFrançois Tigeot 		mstb->tx_slots[txmsg->seqno] = txmsg;
146724edb884SFrançois Tigeot 	}
1468aee94f86SFrançois Tigeot 
1469aee94f86SFrançois Tigeot 	req_type = txmsg->msg[0] & 0x7f;
1470aee94f86SFrançois Tigeot 	if (req_type == DP_CONNECTION_STATUS_NOTIFY ||
1471aee94f86SFrançois Tigeot 		req_type == DP_RESOURCE_STATUS_NOTIFY)
1472aee94f86SFrançois Tigeot 		hdr->broadcast = 1;
1473aee94f86SFrançois Tigeot 	else
147424edb884SFrançois Tigeot 		hdr->broadcast = 0;
147524edb884SFrançois Tigeot 	hdr->path_msg = txmsg->path_msg;
147624edb884SFrançois Tigeot 	hdr->lct = mstb->lct;
147724edb884SFrançois Tigeot 	hdr->lcr = mstb->lct - 1;
147824edb884SFrançois Tigeot 	if (mstb->lct > 1)
147924edb884SFrançois Tigeot 		memcpy(hdr->rad, mstb->rad, mstb->lct / 2);
148024edb884SFrançois Tigeot 	hdr->seqno = txmsg->seqno;
148124edb884SFrançois Tigeot 	return 0;
148224edb884SFrançois Tigeot }
148324edb884SFrançois Tigeot /*
148424edb884SFrançois Tigeot  * process a single block of the next message in the sideband queue
148524edb884SFrançois Tigeot  */
process_single_tx_qlock(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_sideband_msg_tx * txmsg,bool up)148624edb884SFrançois Tigeot static int process_single_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
148724edb884SFrançois Tigeot 				   struct drm_dp_sideband_msg_tx *txmsg,
148824edb884SFrançois Tigeot 				   bool up)
148924edb884SFrançois Tigeot {
149024edb884SFrançois Tigeot 	u8 chunk[48];
149124edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_hdr hdr;
149224edb884SFrançois Tigeot 	int len, space, idx, tosend;
149324edb884SFrançois Tigeot 	int ret;
149424edb884SFrançois Tigeot 
149524edb884SFrançois Tigeot 	memset(&hdr, 0, sizeof(struct drm_dp_sideband_msg_hdr));
149624edb884SFrançois Tigeot 
149724edb884SFrançois Tigeot 	if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED) {
149824edb884SFrançois Tigeot 		txmsg->seqno = -1;
149924edb884SFrançois Tigeot 		txmsg->state = DRM_DP_SIDEBAND_TX_START_SEND;
150024edb884SFrançois Tigeot 	}
150124edb884SFrançois Tigeot 
150224edb884SFrançois Tigeot 	/* make hdr from dst mst - for replies use seqno
150324edb884SFrançois Tigeot 	   otherwise assign one */
150424edb884SFrançois Tigeot 	ret = set_hdr_from_dst_qlock(&hdr, txmsg);
150524edb884SFrançois Tigeot 	if (ret < 0)
150624edb884SFrançois Tigeot 		return ret;
150724edb884SFrançois Tigeot 
150824edb884SFrançois Tigeot 	/* amount left to send in this message */
150924edb884SFrançois Tigeot 	len = txmsg->cur_len - txmsg->cur_offset;
151024edb884SFrançois Tigeot 
151124edb884SFrançois Tigeot 	/* 48 - sideband msg size - 1 byte for data CRC, x header bytes */
151224edb884SFrançois Tigeot 	space = 48 - 1 - drm_dp_calc_sb_hdr_size(&hdr);
151324edb884SFrançois Tigeot 
151424edb884SFrançois Tigeot 	tosend = min(len, space);
151524edb884SFrançois Tigeot 	if (len == txmsg->cur_len)
151624edb884SFrançois Tigeot 		hdr.somt = 1;
151724edb884SFrançois Tigeot 	if (space >= len)
151824edb884SFrançois Tigeot 		hdr.eomt = 1;
151924edb884SFrançois Tigeot 
152024edb884SFrançois Tigeot 
152124edb884SFrançois Tigeot 	hdr.msg_len = tosend + 1;
152224edb884SFrançois Tigeot 	drm_dp_encode_sideband_msg_hdr(&hdr, chunk, &idx);
152324edb884SFrançois Tigeot 	memcpy(&chunk[idx], &txmsg->msg[txmsg->cur_offset], tosend);
152424edb884SFrançois Tigeot 	/* add crc at end */
152524edb884SFrançois Tigeot 	drm_dp_crc_sideband_chunk_req(&chunk[idx], tosend);
152624edb884SFrançois Tigeot 	idx += tosend + 1;
152724edb884SFrançois Tigeot 
152824edb884SFrançois Tigeot 	ret = drm_dp_send_sideband_msg(mgr, up, chunk, idx);
152924edb884SFrançois Tigeot 	if (ret) {
153024edb884SFrançois Tigeot 		DRM_DEBUG_KMS("sideband msg failed to send\n");
153124edb884SFrançois Tigeot 		return ret;
153224edb884SFrançois Tigeot 	}
153324edb884SFrançois Tigeot 
153424edb884SFrançois Tigeot 	txmsg->cur_offset += tosend;
153524edb884SFrançois Tigeot 	if (txmsg->cur_offset == txmsg->cur_len) {
153624edb884SFrançois Tigeot 		txmsg->state = DRM_DP_SIDEBAND_TX_SENT;
153724edb884SFrançois Tigeot 		return 1;
153824edb884SFrançois Tigeot 	}
153924edb884SFrançois Tigeot 	return 0;
154024edb884SFrançois Tigeot }
154124edb884SFrançois Tigeot 
process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr * mgr)154224edb884SFrançois Tigeot static void process_single_down_tx_qlock(struct drm_dp_mst_topology_mgr *mgr)
154324edb884SFrançois Tigeot {
154424edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
154524edb884SFrançois Tigeot 	int ret;
154624edb884SFrançois Tigeot 
1547aee94f86SFrançois Tigeot 	WARN_ON(!mutex_is_locked(&mgr->qlock));
1548aee94f86SFrançois Tigeot 
154924edb884SFrançois Tigeot 	/* construct a chunk from the first msg in the tx_msg queue */
15501dedbd3bSFrançois Tigeot 	if (list_empty(&mgr->tx_msg_downq))
155124edb884SFrançois Tigeot 		return;
155224edb884SFrançois Tigeot 
155324edb884SFrançois Tigeot 	txmsg = list_first_entry(&mgr->tx_msg_downq, struct drm_dp_sideband_msg_tx, next);
155424edb884SFrançois Tigeot 	ret = process_single_tx_qlock(mgr, txmsg, false);
155524edb884SFrançois Tigeot 	if (ret == 1) {
155624edb884SFrançois Tigeot 		/* txmsg is sent it should be in the slots now */
155724edb884SFrançois Tigeot 		list_del(&txmsg->next);
155824edb884SFrançois Tigeot 	} else if (ret) {
155924edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
156024edb884SFrançois Tigeot 		list_del(&txmsg->next);
156124edb884SFrançois Tigeot 		if (txmsg->seqno != -1)
156224edb884SFrançois Tigeot 			txmsg->dst->tx_slots[txmsg->seqno] = NULL;
156324edb884SFrançois Tigeot 		txmsg->state = DRM_DP_SIDEBAND_TX_TIMEOUT;
1564*3f2dd94aSFrançois Tigeot 		wake_up_all(&mgr->tx_waitq);
156524edb884SFrançois Tigeot 	}
156624edb884SFrançois Tigeot }
156724edb884SFrançois Tigeot 
156824edb884SFrançois Tigeot /* called holding qlock */
process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_sideband_msg_tx * txmsg)1569aee94f86SFrançois Tigeot static void process_single_up_tx_qlock(struct drm_dp_mst_topology_mgr *mgr,
1570aee94f86SFrançois Tigeot 				       struct drm_dp_sideband_msg_tx *txmsg)
157124edb884SFrançois Tigeot {
157224edb884SFrançois Tigeot 	int ret;
157324edb884SFrançois Tigeot 
157424edb884SFrançois Tigeot 	/* construct a chunk from the first msg in the tx_msg queue */
157524edb884SFrançois Tigeot 	ret = process_single_tx_qlock(mgr, txmsg, true);
1576aee94f86SFrançois Tigeot 
1577aee94f86SFrançois Tigeot 	if (ret != 1)
157824edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to send msg in q %d\n", ret);
1579aee94f86SFrançois Tigeot 
1580aee94f86SFrançois Tigeot 	txmsg->dst->tx_slots[txmsg->seqno] = NULL;
158124edb884SFrançois Tigeot }
158224edb884SFrançois Tigeot 
drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_sideband_msg_tx * txmsg)158324edb884SFrançois Tigeot static void drm_dp_queue_down_tx(struct drm_dp_mst_topology_mgr *mgr,
158424edb884SFrançois Tigeot 				 struct drm_dp_sideband_msg_tx *txmsg)
158524edb884SFrançois Tigeot {
158624edb884SFrançois Tigeot 	mutex_lock(&mgr->qlock);
158724edb884SFrançois Tigeot 	list_add_tail(&txmsg->next, &mgr->tx_msg_downq);
15881dedbd3bSFrançois Tigeot 	if (list_is_singular(&mgr->tx_msg_downq))
158924edb884SFrançois Tigeot 		process_single_down_tx_qlock(mgr);
159024edb884SFrançois Tigeot 	mutex_unlock(&mgr->qlock);
159124edb884SFrançois Tigeot }
159224edb884SFrançois Tigeot 
drm_dp_send_link_address(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb)1593a05eeebfSFrançois Tigeot static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
159424edb884SFrançois Tigeot 				     struct drm_dp_mst_branch *mstb)
159524edb884SFrançois Tigeot {
159624edb884SFrançois Tigeot 	int len;
159724edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
159824edb884SFrançois Tigeot 	int ret;
159924edb884SFrançois Tigeot 
160024edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
160124edb884SFrançois Tigeot 	if (!txmsg)
1602a05eeebfSFrançois Tigeot 		return;
160324edb884SFrançois Tigeot 
160424edb884SFrançois Tigeot 	txmsg->dst = mstb;
160524edb884SFrançois Tigeot 	len = build_link_address(txmsg);
160624edb884SFrançois Tigeot 
1607a05eeebfSFrançois Tigeot 	mstb->link_address_sent = true;
160824edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
160924edb884SFrançois Tigeot 
161024edb884SFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
161124edb884SFrançois Tigeot 	if (ret > 0) {
161224edb884SFrançois Tigeot 		int i;
161324edb884SFrançois Tigeot 
161424edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1)
161524edb884SFrançois Tigeot 			DRM_DEBUG_KMS("link address nak received\n");
161624edb884SFrançois Tigeot 		else {
161724edb884SFrançois Tigeot 			DRM_DEBUG_KMS("link address reply: %d\n", txmsg->reply.u.link_addr.nports);
161824edb884SFrançois Tigeot 			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
161924edb884SFrançois Tigeot 				DRM_DEBUG_KMS("port %d: input %d, pdt: %d, pn: %d, dpcd_rev: %02x, mcs: %d, ddps: %d, ldps %d, sdp %d/%d\n", i,
162024edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].input_port,
162124edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].peer_device_type,
162224edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].port_number,
162324edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].dpcd_revision,
162424edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].mcs,
162524edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].ddps,
162624edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].legacy_device_plug_status,
162724edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
162824edb884SFrançois Tigeot 				       txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
162924edb884SFrançois Tigeot 			}
1630aee94f86SFrançois Tigeot 
1631aee94f86SFrançois Tigeot 			drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid);
1632aee94f86SFrançois Tigeot 
163324edb884SFrançois Tigeot 			for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
163424edb884SFrançois Tigeot 				drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
163524edb884SFrançois Tigeot 			}
163624edb884SFrançois Tigeot 			(*mgr->cbs->hotplug)(mgr);
163724edb884SFrançois Tigeot 		}
1638a05eeebfSFrançois Tigeot 	} else {
1639a05eeebfSFrançois Tigeot 		mstb->link_address_sent = false;
164024edb884SFrançois Tigeot 		DRM_DEBUG_KMS("link address failed %d\n", ret);
1641a05eeebfSFrançois Tigeot 	}
164224edb884SFrançois Tigeot 
164324edb884SFrançois Tigeot 	kfree(txmsg);
164424edb884SFrançois Tigeot }
164524edb884SFrançois Tigeot 
drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb,struct drm_dp_mst_port * port)164624edb884SFrançois Tigeot static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
164724edb884SFrançois Tigeot 					   struct drm_dp_mst_branch *mstb,
164824edb884SFrançois Tigeot 					   struct drm_dp_mst_port *port)
164924edb884SFrançois Tigeot {
165024edb884SFrançois Tigeot 	int len;
165124edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
165224edb884SFrançois Tigeot 	int ret;
165324edb884SFrançois Tigeot 
165424edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
165524edb884SFrançois Tigeot 	if (!txmsg)
165624edb884SFrançois Tigeot 		return -ENOMEM;
165724edb884SFrançois Tigeot 
165824edb884SFrançois Tigeot 	txmsg->dst = mstb;
165924edb884SFrançois Tigeot 	len = build_enum_path_resources(txmsg, port->port_num);
166024edb884SFrançois Tigeot 
166124edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
166224edb884SFrançois Tigeot 
166324edb884SFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
166424edb884SFrançois Tigeot 	if (ret > 0) {
166524edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1)
166624edb884SFrançois Tigeot 			DRM_DEBUG_KMS("enum path resources nak received\n");
166724edb884SFrançois Tigeot 		else {
166824edb884SFrançois Tigeot 			if (port->port_num != txmsg->reply.u.path_resources.port_number)
166924edb884SFrançois Tigeot 				DRM_ERROR("got incorrect port in response\n");
167024edb884SFrançois Tigeot 			DRM_DEBUG_KMS("enum path resources %d: %d %d\n", txmsg->reply.u.path_resources.port_number, txmsg->reply.u.path_resources.full_payload_bw_number,
167124edb884SFrançois Tigeot 			       txmsg->reply.u.path_resources.avail_payload_bw_number);
167224edb884SFrançois Tigeot 			port->available_pbn = txmsg->reply.u.path_resources.avail_payload_bw_number;
167324edb884SFrançois Tigeot 		}
167424edb884SFrançois Tigeot 	}
167524edb884SFrançois Tigeot 
167624edb884SFrançois Tigeot 	kfree(txmsg);
167724edb884SFrançois Tigeot 	return 0;
167824edb884SFrançois Tigeot }
167924edb884SFrançois Tigeot 
drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch * mstb)1680aee94f86SFrançois Tigeot static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb)
1681aee94f86SFrançois Tigeot {
1682aee94f86SFrançois Tigeot 	if (!mstb->port_parent)
1683aee94f86SFrançois Tigeot 		return NULL;
1684aee94f86SFrançois Tigeot 
1685aee94f86SFrançois Tigeot 	if (mstb->port_parent->mstb != mstb)
1686aee94f86SFrançois Tigeot 		return mstb->port_parent;
1687aee94f86SFrançois Tigeot 
1688aee94f86SFrançois Tigeot 	return drm_dp_get_last_connected_port_to_mstb(mstb->port_parent->parent);
1689aee94f86SFrançois Tigeot }
1690aee94f86SFrançois Tigeot 
drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb,int * port_num)1691aee94f86SFrançois Tigeot static struct drm_dp_mst_branch *drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr *mgr,
1692aee94f86SFrançois Tigeot 									 struct drm_dp_mst_branch *mstb,
1693aee94f86SFrançois Tigeot 									 int *port_num)
1694aee94f86SFrançois Tigeot {
1695aee94f86SFrançois Tigeot 	struct drm_dp_mst_branch *rmstb = NULL;
1696aee94f86SFrançois Tigeot 	struct drm_dp_mst_port *found_port;
1697aee94f86SFrançois Tigeot 	mutex_lock(&mgr->lock);
1698aee94f86SFrançois Tigeot 	if (mgr->mst_primary) {
1699aee94f86SFrançois Tigeot 		found_port = drm_dp_get_last_connected_port_to_mstb(mstb);
1700aee94f86SFrançois Tigeot 
1701aee94f86SFrançois Tigeot 		if (found_port) {
1702aee94f86SFrançois Tigeot 			rmstb = found_port->parent;
1703aee94f86SFrançois Tigeot 			kref_get(&rmstb->kref);
1704aee94f86SFrançois Tigeot 			*port_num = found_port->port_num;
1705aee94f86SFrançois Tigeot 		}
1706aee94f86SFrançois Tigeot 	}
1707aee94f86SFrançois Tigeot 	mutex_unlock(&mgr->lock);
1708aee94f86SFrançois Tigeot 	return rmstb;
1709aee94f86SFrançois Tigeot }
1710aee94f86SFrançois Tigeot 
drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int id,int pbn)171124edb884SFrançois Tigeot static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
171224edb884SFrançois Tigeot 				   struct drm_dp_mst_port *port,
171324edb884SFrançois Tigeot 				   int id,
171424edb884SFrançois Tigeot 				   int pbn)
171524edb884SFrançois Tigeot {
171624edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
171724edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
1718aee94f86SFrançois Tigeot 	int len, ret, port_num;
1719aee94f86SFrançois Tigeot 	u8 sinks[DRM_DP_MAX_SDP_STREAMS];
1720aee94f86SFrançois Tigeot 	int i;
172124edb884SFrançois Tigeot 
1722c0e85e96SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
1723c0e85e96SFrançois Tigeot 	if (!port)
1724c0e85e96SFrançois Tigeot 		return -EINVAL;
1725c0e85e96SFrançois Tigeot 
1726aee94f86SFrançois Tigeot 	port_num = port->port_num;
172724edb884SFrançois Tigeot 	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
1728aee94f86SFrançois Tigeot 	if (!mstb) {
1729aee94f86SFrançois Tigeot 		mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num);
1730aee94f86SFrançois Tigeot 
1731c0e85e96SFrançois Tigeot 		if (!mstb) {
1732c0e85e96SFrançois Tigeot 			drm_dp_put_port(port);
173324edb884SFrançois Tigeot 			return -EINVAL;
1734aee94f86SFrançois Tigeot 		}
1735c0e85e96SFrançois Tigeot 	}
173624edb884SFrançois Tigeot 
173724edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
173824edb884SFrançois Tigeot 	if (!txmsg) {
173924edb884SFrançois Tigeot 		ret = -ENOMEM;
174024edb884SFrançois Tigeot 		goto fail_put;
174124edb884SFrançois Tigeot 	}
174224edb884SFrançois Tigeot 
1743aee94f86SFrançois Tigeot 	for (i = 0; i < port->num_sdp_streams; i++)
1744aee94f86SFrançois Tigeot 		sinks[i] = i;
1745aee94f86SFrançois Tigeot 
174624edb884SFrançois Tigeot 	txmsg->dst = mstb;
1747aee94f86SFrançois Tigeot 	len = build_allocate_payload(txmsg, port_num,
174824edb884SFrançois Tigeot 				     id,
1749aee94f86SFrançois Tigeot 				     pbn, port->num_sdp_streams, sinks);
175024edb884SFrançois Tigeot 
175124edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
175224edb884SFrançois Tigeot 
175324edb884SFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
175424edb884SFrançois Tigeot 	if (ret > 0) {
175524edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1) {
175624edb884SFrançois Tigeot 			ret = -EINVAL;
175724edb884SFrançois Tigeot 		} else
175824edb884SFrançois Tigeot 			ret = 0;
175924edb884SFrançois Tigeot 	}
176024edb884SFrançois Tigeot 	kfree(txmsg);
176124edb884SFrançois Tigeot fail_put:
176224edb884SFrançois Tigeot 	drm_dp_put_mst_branch_device(mstb);
1763c0e85e96SFrançois Tigeot 	drm_dp_put_port(port);
176424edb884SFrançois Tigeot 	return ret;
176524edb884SFrançois Tigeot }
176624edb884SFrançois Tigeot 
drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,bool power_up)1767*3f2dd94aSFrançois Tigeot int drm_dp_send_power_updown_phy(struct drm_dp_mst_topology_mgr *mgr,
1768*3f2dd94aSFrançois Tigeot 				 struct drm_dp_mst_port *port, bool power_up)
1769*3f2dd94aSFrançois Tigeot {
1770*3f2dd94aSFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
1771*3f2dd94aSFrançois Tigeot 	int len, ret;
1772*3f2dd94aSFrançois Tigeot 
1773*3f2dd94aSFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
1774*3f2dd94aSFrançois Tigeot 	if (!port)
1775*3f2dd94aSFrançois Tigeot 		return -EINVAL;
1776*3f2dd94aSFrançois Tigeot 
1777*3f2dd94aSFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
1778*3f2dd94aSFrançois Tigeot 	if (!txmsg) {
1779*3f2dd94aSFrançois Tigeot 		drm_dp_put_port(port);
1780*3f2dd94aSFrançois Tigeot 		return -ENOMEM;
1781*3f2dd94aSFrançois Tigeot 	}
1782*3f2dd94aSFrançois Tigeot 
1783*3f2dd94aSFrançois Tigeot 	txmsg->dst = port->parent;
1784*3f2dd94aSFrançois Tigeot 	len = build_power_updown_phy(txmsg, port->port_num, power_up);
1785*3f2dd94aSFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
1786*3f2dd94aSFrançois Tigeot 
1787*3f2dd94aSFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(port->parent, txmsg);
1788*3f2dd94aSFrançois Tigeot 	if (ret > 0) {
1789*3f2dd94aSFrançois Tigeot 		if (txmsg->reply.reply_type == 1)
1790*3f2dd94aSFrançois Tigeot 			ret = -EINVAL;
1791*3f2dd94aSFrançois Tigeot 		else
1792*3f2dd94aSFrançois Tigeot 			ret = 0;
1793*3f2dd94aSFrançois Tigeot 	}
1794*3f2dd94aSFrançois Tigeot 	kfree(txmsg);
1795*3f2dd94aSFrançois Tigeot 	drm_dp_put_port(port);
1796*3f2dd94aSFrançois Tigeot 
1797*3f2dd94aSFrançois Tigeot 	return ret;
1798*3f2dd94aSFrançois Tigeot }
1799*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_dp_send_power_updown_phy);
1800*3f2dd94aSFrançois Tigeot 
drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr * mgr,int id,struct drm_dp_payload * payload)180124edb884SFrançois Tigeot static int drm_dp_create_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
180224edb884SFrançois Tigeot 				       int id,
180324edb884SFrançois Tigeot 				       struct drm_dp_payload *payload)
180424edb884SFrançois Tigeot {
180524edb884SFrançois Tigeot 	int ret;
180624edb884SFrançois Tigeot 
180724edb884SFrançois Tigeot 	ret = drm_dp_dpcd_write_payload(mgr, id, payload);
180824edb884SFrançois Tigeot 	if (ret < 0) {
180924edb884SFrançois Tigeot 		payload->payload_state = 0;
181024edb884SFrançois Tigeot 		return ret;
181124edb884SFrançois Tigeot 	}
181224edb884SFrançois Tigeot 	payload->payload_state = DP_PAYLOAD_LOCAL;
181324edb884SFrançois Tigeot 	return 0;
181424edb884SFrançois Tigeot }
181524edb884SFrançois Tigeot 
drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int id,struct drm_dp_payload * payload)181624edb884SFrançois Tigeot static int drm_dp_create_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
181724edb884SFrançois Tigeot 				       struct drm_dp_mst_port *port,
181824edb884SFrançois Tigeot 				       int id,
181924edb884SFrançois Tigeot 				       struct drm_dp_payload *payload)
182024edb884SFrançois Tigeot {
182124edb884SFrançois Tigeot 	int ret;
182224edb884SFrançois Tigeot 	ret = drm_dp_payload_send_msg(mgr, port, id, port->vcpi.pbn);
182324edb884SFrançois Tigeot 	if (ret < 0)
182424edb884SFrançois Tigeot 		return ret;
182524edb884SFrançois Tigeot 	payload->payload_state = DP_PAYLOAD_REMOTE;
182624edb884SFrançois Tigeot 	return ret;
182724edb884SFrançois Tigeot }
182824edb884SFrançois Tigeot 
drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int id,struct drm_dp_payload * payload)182924edb884SFrançois Tigeot static int drm_dp_destroy_payload_step1(struct drm_dp_mst_topology_mgr *mgr,
183024edb884SFrançois Tigeot 					struct drm_dp_mst_port *port,
183124edb884SFrançois Tigeot 					int id,
183224edb884SFrançois Tigeot 					struct drm_dp_payload *payload)
183324edb884SFrançois Tigeot {
183424edb884SFrançois Tigeot 	DRM_DEBUG_KMS("\n");
183524edb884SFrançois Tigeot 	/* its okay for these to fail */
183624edb884SFrançois Tigeot 	if (port) {
183724edb884SFrançois Tigeot 		drm_dp_payload_send_msg(mgr, port, id, 0);
183824edb884SFrançois Tigeot 	}
183924edb884SFrançois Tigeot 
184024edb884SFrançois Tigeot 	drm_dp_dpcd_write_payload(mgr, id, payload);
1841aee94f86SFrançois Tigeot 	payload->payload_state = DP_PAYLOAD_DELETE_LOCAL;
184224edb884SFrançois Tigeot 	return 0;
184324edb884SFrançois Tigeot }
184424edb884SFrançois Tigeot 
drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr * mgr,int id,struct drm_dp_payload * payload)184524edb884SFrançois Tigeot static int drm_dp_destroy_payload_step2(struct drm_dp_mst_topology_mgr *mgr,
184624edb884SFrançois Tigeot 					int id,
184724edb884SFrançois Tigeot 					struct drm_dp_payload *payload)
184824edb884SFrançois Tigeot {
184924edb884SFrançois Tigeot 	payload->payload_state = 0;
185024edb884SFrançois Tigeot 	return 0;
185124edb884SFrançois Tigeot }
185224edb884SFrançois Tigeot 
185324edb884SFrançois Tigeot /**
185424edb884SFrançois Tigeot  * drm_dp_update_payload_part1() - Execute payload update part 1
185524edb884SFrançois Tigeot  * @mgr: manager to use.
185624edb884SFrançois Tigeot  *
185724edb884SFrançois Tigeot  * This iterates over all proposed virtual channels, and tries to
185824edb884SFrançois Tigeot  * allocate space in the link for them. For 0->slots transitions,
185924edb884SFrançois Tigeot  * this step just writes the VCPI to the MST device. For slots->0
186024edb884SFrançois Tigeot  * transitions, this writes the updated VCPIs and removes the
186124edb884SFrançois Tigeot  * remote VC payloads.
186224edb884SFrançois Tigeot  *
186324edb884SFrançois Tigeot  * after calling this the driver should generate ACT and payload
186424edb884SFrançois Tigeot  * packets.
186524edb884SFrançois Tigeot  */
drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr * mgr)186624edb884SFrançois Tigeot int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
186724edb884SFrançois Tigeot {
1868aee94f86SFrançois Tigeot 	int i, j;
186924edb884SFrançois Tigeot 	int cur_slots = 1;
187024edb884SFrançois Tigeot 	struct drm_dp_payload req_payload;
187124edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
187224edb884SFrançois Tigeot 
187324edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
187424edb884SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
187524edb884SFrançois Tigeot 		/* solve the current payloads - compare to the hw ones
187624edb884SFrançois Tigeot 		   - update the hw view */
187724edb884SFrançois Tigeot 		req_payload.start_slot = cur_slots;
187824edb884SFrançois Tigeot 		if (mgr->proposed_vcpis[i]) {
187924edb884SFrançois Tigeot 			port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
1880c0e85e96SFrançois Tigeot 			port = drm_dp_get_validated_port_ref(mgr, port);
1881c0e85e96SFrançois Tigeot 			if (!port) {
1882c0e85e96SFrançois Tigeot 				mutex_unlock(&mgr->payload_lock);
1883c0e85e96SFrançois Tigeot 				return -EINVAL;
1884c0e85e96SFrançois Tigeot 			}
188524edb884SFrançois Tigeot 			req_payload.num_slots = mgr->proposed_vcpis[i]->num_slots;
1886aee94f86SFrançois Tigeot 			req_payload.vcpi = mgr->proposed_vcpis[i]->vcpi;
188724edb884SFrançois Tigeot 		} else {
188824edb884SFrançois Tigeot 			port = NULL;
188924edb884SFrançois Tigeot 			req_payload.num_slots = 0;
189024edb884SFrançois Tigeot 		}
1891aee94f86SFrançois Tigeot 
1892aee94f86SFrançois Tigeot 		if (mgr->payloads[i].start_slot != req_payload.start_slot) {
1893aee94f86SFrançois Tigeot 			mgr->payloads[i].start_slot = req_payload.start_slot;
1894aee94f86SFrançois Tigeot 		}
189524edb884SFrançois Tigeot 		/* work out what is required to happen with this payload */
1896aee94f86SFrançois Tigeot 		if (mgr->payloads[i].num_slots != req_payload.num_slots) {
189724edb884SFrançois Tigeot 
189824edb884SFrançois Tigeot 			/* need to push an update for this payload */
189924edb884SFrançois Tigeot 			if (req_payload.num_slots) {
1900aee94f86SFrançois Tigeot 				drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload);
190124edb884SFrançois Tigeot 				mgr->payloads[i].num_slots = req_payload.num_slots;
1902aee94f86SFrançois Tigeot 				mgr->payloads[i].vcpi = req_payload.vcpi;
190324edb884SFrançois Tigeot 			} else if (mgr->payloads[i].num_slots) {
190424edb884SFrançois Tigeot 				mgr->payloads[i].num_slots = 0;
19054be47400SFrançois Tigeot 				drm_dp_destroy_payload_step1(mgr, port, mgr->payloads[i].vcpi, &mgr->payloads[i]);
190624edb884SFrançois Tigeot 				req_payload.payload_state = mgr->payloads[i].payload_state;
1907aee94f86SFrançois Tigeot 				mgr->payloads[i].start_slot = 0;
1908aee94f86SFrançois Tigeot 			}
190924edb884SFrançois Tigeot 			mgr->payloads[i].payload_state = req_payload.payload_state;
191024edb884SFrançois Tigeot 		}
191124edb884SFrançois Tigeot 		cur_slots += req_payload.num_slots;
1912c0e85e96SFrançois Tigeot 
1913c0e85e96SFrançois Tigeot 		if (port)
1914c0e85e96SFrançois Tigeot 			drm_dp_put_port(port);
191524edb884SFrançois Tigeot 	}
1916aee94f86SFrançois Tigeot 
1917aee94f86SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
1918aee94f86SFrançois Tigeot 		if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
1919aee94f86SFrançois Tigeot 			DRM_DEBUG_KMS("removing payload %d\n", i);
1920aee94f86SFrançois Tigeot 			for (j = i; j < mgr->max_payloads - 1; j++) {
1921aee94f86SFrançois Tigeot 				memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload));
1922aee94f86SFrançois Tigeot 				mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1];
1923aee94f86SFrançois Tigeot 				if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) {
1924aee94f86SFrançois Tigeot 					set_bit(j + 1, &mgr->payload_mask);
1925aee94f86SFrançois Tigeot 				} else {
1926aee94f86SFrançois Tigeot 					clear_bit(j + 1, &mgr->payload_mask);
1927aee94f86SFrançois Tigeot 				}
1928aee94f86SFrançois Tigeot 			}
1929aee94f86SFrançois Tigeot 			memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload));
1930aee94f86SFrançois Tigeot 			mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL;
1931aee94f86SFrançois Tigeot 			clear_bit(mgr->max_payloads, &mgr->payload_mask);
1932aee94f86SFrançois Tigeot 
1933aee94f86SFrançois Tigeot 		}
1934aee94f86SFrançois Tigeot 	}
193524edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
193624edb884SFrançois Tigeot 
193724edb884SFrançois Tigeot 	return 0;
193824edb884SFrançois Tigeot }
193924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_update_payload_part1);
194024edb884SFrançois Tigeot 
194124edb884SFrançois Tigeot /**
194224edb884SFrançois Tigeot  * drm_dp_update_payload_part2() - Execute payload update part 2
194324edb884SFrançois Tigeot  * @mgr: manager to use.
194424edb884SFrançois Tigeot  *
194524edb884SFrançois Tigeot  * This iterates over all proposed virtual channels, and tries to
194624edb884SFrançois Tigeot  * allocate space in the link for them. For 0->slots transitions,
194724edb884SFrançois Tigeot  * this step writes the remote VC payload commands. For slots->0
194824edb884SFrançois Tigeot  * this just resets some internal state.
194924edb884SFrançois Tigeot  */
drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr * mgr)195024edb884SFrançois Tigeot int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
195124edb884SFrançois Tigeot {
195224edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
195324edb884SFrançois Tigeot 	int i;
1954aee94f86SFrançois Tigeot 	int ret = 0;
195524edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
195624edb884SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
195724edb884SFrançois Tigeot 
195824edb884SFrançois Tigeot 		if (!mgr->proposed_vcpis[i])
195924edb884SFrançois Tigeot 			continue;
196024edb884SFrançois Tigeot 
196124edb884SFrançois Tigeot 		port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
196224edb884SFrançois Tigeot 
196324edb884SFrançois Tigeot 		DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state);
196424edb884SFrançois Tigeot 		if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) {
1965aee94f86SFrançois Tigeot 			ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
196624edb884SFrançois Tigeot 		} else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) {
1967aee94f86SFrançois Tigeot 			ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]);
196824edb884SFrançois Tigeot 		}
196924edb884SFrançois Tigeot 		if (ret) {
197024edb884SFrançois Tigeot 			mutex_unlock(&mgr->payload_lock);
197124edb884SFrançois Tigeot 			return ret;
197224edb884SFrançois Tigeot 		}
197324edb884SFrançois Tigeot 	}
197424edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
197524edb884SFrançois Tigeot 	return 0;
197624edb884SFrançois Tigeot }
197724edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_update_payload_part2);
197824edb884SFrançois Tigeot 
197924edb884SFrançois Tigeot #if 0 /* unused as of yet */
198024edb884SFrançois Tigeot static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
198124edb884SFrançois Tigeot 				 struct drm_dp_mst_port *port,
198224edb884SFrançois Tigeot 				 int offset, int size)
198324edb884SFrançois Tigeot {
198424edb884SFrançois Tigeot 	int len;
198524edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
198624edb884SFrançois Tigeot 
198724edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
198824edb884SFrançois Tigeot 	if (!txmsg)
198924edb884SFrançois Tigeot 		return -ENOMEM;
199024edb884SFrançois Tigeot 
199124edb884SFrançois Tigeot 	len = build_dpcd_read(txmsg, port->port_num, 0, 8);
199224edb884SFrançois Tigeot 	txmsg->dst = port->parent;
199324edb884SFrançois Tigeot 
199424edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
199524edb884SFrançois Tigeot 
199624edb884SFrançois Tigeot 	return 0;
199724edb884SFrançois Tigeot }
199824edb884SFrançois Tigeot #endif
199924edb884SFrançois Tigeot 
drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int offset,int size,u8 * bytes)200024edb884SFrançois Tigeot static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
200124edb884SFrançois Tigeot 				  struct drm_dp_mst_port *port,
200224edb884SFrançois Tigeot 				  int offset, int size, u8 *bytes)
200324edb884SFrançois Tigeot {
200424edb884SFrançois Tigeot 	int len;
200524edb884SFrançois Tigeot 	int ret;
200624edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
200724edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
200824edb884SFrançois Tigeot 
200924edb884SFrançois Tigeot 	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
201024edb884SFrançois Tigeot 	if (!mstb)
201124edb884SFrançois Tigeot 		return -EINVAL;
201224edb884SFrançois Tigeot 
201324edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
201424edb884SFrançois Tigeot 	if (!txmsg) {
201524edb884SFrançois Tigeot 		ret = -ENOMEM;
201624edb884SFrançois Tigeot 		goto fail_put;
201724edb884SFrançois Tigeot 	}
201824edb884SFrançois Tigeot 
201924edb884SFrançois Tigeot 	len = build_dpcd_write(txmsg, port->port_num, offset, size, bytes);
202024edb884SFrançois Tigeot 	txmsg->dst = mstb;
202124edb884SFrançois Tigeot 
202224edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
202324edb884SFrançois Tigeot 
202424edb884SFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
202524edb884SFrançois Tigeot 	if (ret > 0) {
202624edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1) {
202724edb884SFrançois Tigeot 			ret = -EINVAL;
202824edb884SFrançois Tigeot 		} else
202924edb884SFrançois Tigeot 			ret = 0;
203024edb884SFrançois Tigeot 	}
203124edb884SFrançois Tigeot 	kfree(txmsg);
203224edb884SFrançois Tigeot fail_put:
203324edb884SFrançois Tigeot 	drm_dp_put_mst_branch_device(mstb);
203424edb884SFrançois Tigeot 	return ret;
203524edb884SFrançois Tigeot }
203624edb884SFrançois Tigeot 
drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx * msg,u8 req_type)203724edb884SFrançois Tigeot static int drm_dp_encode_up_ack_reply(struct drm_dp_sideband_msg_tx *msg, u8 req_type)
203824edb884SFrançois Tigeot {
203924edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_reply_body reply;
204024edb884SFrançois Tigeot 
2041aee94f86SFrançois Tigeot 	reply.reply_type = 0;
204224edb884SFrançois Tigeot 	reply.req_type = req_type;
204324edb884SFrançois Tigeot 	drm_dp_encode_sideband_reply(&reply, msg);
204424edb884SFrançois Tigeot 	return 0;
204524edb884SFrançois Tigeot }
204624edb884SFrançois Tigeot 
drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_branch * mstb,int req_type,int seqno,bool broadcast)204724edb884SFrançois Tigeot static int drm_dp_send_up_ack_reply(struct drm_dp_mst_topology_mgr *mgr,
204824edb884SFrançois Tigeot 				    struct drm_dp_mst_branch *mstb,
204924edb884SFrançois Tigeot 				    int req_type, int seqno, bool broadcast)
205024edb884SFrançois Tigeot {
205124edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg;
205224edb884SFrançois Tigeot 
205324edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
205424edb884SFrançois Tigeot 	if (!txmsg)
205524edb884SFrançois Tigeot 		return -ENOMEM;
205624edb884SFrançois Tigeot 
205724edb884SFrançois Tigeot 	txmsg->dst = mstb;
205824edb884SFrançois Tigeot 	txmsg->seqno = seqno;
205924edb884SFrançois Tigeot 	drm_dp_encode_up_ack_reply(txmsg, req_type);
206024edb884SFrançois Tigeot 
206124edb884SFrançois Tigeot 	mutex_lock(&mgr->qlock);
2062aee94f86SFrançois Tigeot 
2063aee94f86SFrançois Tigeot 	process_single_up_tx_qlock(mgr, txmsg);
2064aee94f86SFrançois Tigeot 
206524edb884SFrançois Tigeot 	mutex_unlock(&mgr->qlock);
2066aee94f86SFrançois Tigeot 
2067aee94f86SFrançois Tigeot 	kfree(txmsg);
206824edb884SFrançois Tigeot 	return 0;
206924edb884SFrançois Tigeot }
207024edb884SFrançois Tigeot 
drm_dp_get_vc_payload_bw(int dp_link_bw,int dp_link_count,int * out)20712c9916cdSFrançois Tigeot static bool drm_dp_get_vc_payload_bw(int dp_link_bw,
20722c9916cdSFrançois Tigeot 				     int dp_link_count,
20732c9916cdSFrançois Tigeot 				     int *out)
207424edb884SFrançois Tigeot {
207524edb884SFrançois Tigeot 	switch (dp_link_bw) {
20762c9916cdSFrançois Tigeot 	default:
20772c9916cdSFrançois Tigeot 		DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n",
20782c9916cdSFrançois Tigeot 			      dp_link_bw, dp_link_count);
20792c9916cdSFrançois Tigeot 		return false;
20802c9916cdSFrançois Tigeot 
208124edb884SFrançois Tigeot 	case DP_LINK_BW_1_62:
20822c9916cdSFrançois Tigeot 		*out = 3 * dp_link_count;
20832c9916cdSFrançois Tigeot 		break;
208424edb884SFrançois Tigeot 	case DP_LINK_BW_2_7:
20852c9916cdSFrançois Tigeot 		*out = 5 * dp_link_count;
20862c9916cdSFrançois Tigeot 		break;
208724edb884SFrançois Tigeot 	case DP_LINK_BW_5_4:
20882c9916cdSFrançois Tigeot 		*out = 10 * dp_link_count;
20892c9916cdSFrançois Tigeot 		break;
209024edb884SFrançois Tigeot 	}
20912c9916cdSFrançois Tigeot 	return true;
209224edb884SFrançois Tigeot }
209324edb884SFrançois Tigeot 
209424edb884SFrançois Tigeot /**
209524edb884SFrançois Tigeot  * drm_dp_mst_topology_mgr_set_mst() - Set the MST state for a topology manager
209624edb884SFrançois Tigeot  * @mgr: manager to set state for
209724edb884SFrançois Tigeot  * @mst_state: true to enable MST on this connector - false to disable.
209824edb884SFrançois Tigeot  *
209924edb884SFrançois Tigeot  * This is called by the driver when it detects an MST capable device plugged
210024edb884SFrançois Tigeot  * into a DP MST capable port, or when a DP MST capable device is unplugged.
210124edb884SFrançois Tigeot  */
drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr * mgr,bool mst_state)210224edb884SFrançois Tigeot int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
210324edb884SFrançois Tigeot {
210424edb884SFrançois Tigeot 	int ret = 0;
210524edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb = NULL;
210624edb884SFrançois Tigeot 
210724edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
210824edb884SFrançois Tigeot 	if (mst_state == mgr->mst_state)
210924edb884SFrançois Tigeot 		goto out_unlock;
211024edb884SFrançois Tigeot 
211124edb884SFrançois Tigeot 	mgr->mst_state = mst_state;
211224edb884SFrançois Tigeot 	/* set the device into MST mode */
211324edb884SFrançois Tigeot 	if (mst_state) {
211424edb884SFrançois Tigeot 		WARN_ON(mgr->mst_primary);
211524edb884SFrançois Tigeot 
211624edb884SFrançois Tigeot 		/* get dpcd info */
211724edb884SFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
211824edb884SFrançois Tigeot 		if (ret != DP_RECEIVER_CAP_SIZE) {
211924edb884SFrançois Tigeot 			DRM_DEBUG_KMS("failed to read DPCD\n");
212024edb884SFrançois Tigeot 			goto out_unlock;
212124edb884SFrançois Tigeot 		}
212224edb884SFrançois Tigeot 
21232c9916cdSFrançois Tigeot 		if (!drm_dp_get_vc_payload_bw(mgr->dpcd[1],
21242c9916cdSFrançois Tigeot 					      mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK,
21252c9916cdSFrançois Tigeot 					      &mgr->pbn_div)) {
21262c9916cdSFrançois Tigeot 			ret = -EINVAL;
21272c9916cdSFrançois Tigeot 			goto out_unlock;
21282c9916cdSFrançois Tigeot 		}
21292c9916cdSFrançois Tigeot 
213024edb884SFrançois Tigeot 		/* add initial branch device at LCT 1 */
213124edb884SFrançois Tigeot 		mstb = drm_dp_add_mst_branch_device(1, NULL);
213224edb884SFrançois Tigeot 		if (mstb == NULL) {
213324edb884SFrançois Tigeot 			ret = -ENOMEM;
213424edb884SFrançois Tigeot 			goto out_unlock;
213524edb884SFrançois Tigeot 		}
213624edb884SFrançois Tigeot 		mstb->mgr = mgr;
213724edb884SFrançois Tigeot 
213824edb884SFrançois Tigeot 		/* give this the main reference */
213924edb884SFrançois Tigeot 		mgr->mst_primary = mstb;
214024edb884SFrançois Tigeot 		kref_get(&mgr->mst_primary->kref);
214124edb884SFrançois Tigeot 
214224edb884SFrançois Tigeot 		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
214324edb884SFrançois Tigeot 							 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
214424edb884SFrançois Tigeot 		if (ret < 0) {
214524edb884SFrançois Tigeot 			goto out_unlock;
214624edb884SFrançois Tigeot 		}
214724edb884SFrançois Tigeot 
2148aee94f86SFrançois Tigeot 		{
2149aee94f86SFrançois Tigeot 			struct drm_dp_payload reset_pay;
2150aee94f86SFrançois Tigeot 			reset_pay.start_slot = 0;
2151aee94f86SFrançois Tigeot 			reset_pay.num_slots = 0x3f;
2152aee94f86SFrançois Tigeot 			drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
215324edb884SFrançois Tigeot 		}
215424edb884SFrançois Tigeot 
215524edb884SFrançois Tigeot 		queue_work(system_long_wq, &mgr->work);
215624edb884SFrançois Tigeot 
215724edb884SFrançois Tigeot 		ret = 0;
215824edb884SFrançois Tigeot 	} else {
215924edb884SFrançois Tigeot 		/* disable MST on the device */
216024edb884SFrançois Tigeot 		mstb = mgr->mst_primary;
216124edb884SFrançois Tigeot 		mgr->mst_primary = NULL;
216224edb884SFrançois Tigeot 		/* this can fail if the device is gone */
216324edb884SFrançois Tigeot 		drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
216424edb884SFrançois Tigeot 		ret = 0;
216524edb884SFrançois Tigeot 		memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload));
216624edb884SFrançois Tigeot 		mgr->payload_mask = 0;
216724edb884SFrançois Tigeot 		set_bit(0, &mgr->payload_mask);
2168aee94f86SFrançois Tigeot 		mgr->vcpi_mask = 0;
216924edb884SFrançois Tigeot 	}
217024edb884SFrançois Tigeot 
217124edb884SFrançois Tigeot out_unlock:
217224edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
217324edb884SFrançois Tigeot 	if (mstb)
217424edb884SFrançois Tigeot 		drm_dp_put_mst_branch_device(mstb);
217524edb884SFrançois Tigeot 	return ret;
217624edb884SFrançois Tigeot 
217724edb884SFrançois Tigeot }
217824edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_topology_mgr_set_mst);
217924edb884SFrançois Tigeot 
218024edb884SFrançois Tigeot /**
218124edb884SFrançois Tigeot  * drm_dp_mst_topology_mgr_suspend() - suspend the MST manager
218224edb884SFrançois Tigeot  * @mgr: manager to suspend
218324edb884SFrançois Tigeot  *
218424edb884SFrançois Tigeot  * This function tells the MST device that we can't handle UP messages
218524edb884SFrançois Tigeot  * anymore. This should stop it from sending any since we are suspended.
218624edb884SFrançois Tigeot  */
drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr * mgr)218724edb884SFrançois Tigeot void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr)
218824edb884SFrançois Tigeot {
218924edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
219024edb884SFrançois Tigeot 	drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
219124edb884SFrançois Tigeot 			   DP_MST_EN | DP_UPSTREAM_IS_SRC);
219224edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
2193a05eeebfSFrançois Tigeot 	flush_work(&mgr->work);
2194a05eeebfSFrançois Tigeot 	flush_work(&mgr->destroy_connector_work);
219524edb884SFrançois Tigeot }
219624edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_topology_mgr_suspend);
219724edb884SFrançois Tigeot 
219824edb884SFrançois Tigeot /**
219924edb884SFrançois Tigeot  * drm_dp_mst_topology_mgr_resume() - resume the MST manager
220024edb884SFrançois Tigeot  * @mgr: manager to resume
220124edb884SFrançois Tigeot  *
220224edb884SFrançois Tigeot  * This will fetch DPCD and see if the device is still there,
220324edb884SFrançois Tigeot  * if it is, it will rewrite the MSTM control bits, and return.
220424edb884SFrançois Tigeot  *
220524edb884SFrançois Tigeot  * if the device fails this returns -1, and the driver should do
220624edb884SFrançois Tigeot  * a full MST reprobe, in case we were undocked.
220724edb884SFrançois Tigeot  */
drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr * mgr)220824edb884SFrançois Tigeot int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr)
220924edb884SFrançois Tigeot {
221024edb884SFrançois Tigeot 	int ret = 0;
221124edb884SFrançois Tigeot 
221224edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
221324edb884SFrançois Tigeot 
221424edb884SFrançois Tigeot 	if (mgr->mst_primary) {
221524edb884SFrançois Tigeot 		int sret;
2216c0e85e96SFrançois Tigeot 		u8 guid[16];
2217c0e85e96SFrançois Tigeot 
221824edb884SFrançois Tigeot 		sret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, mgr->dpcd, DP_RECEIVER_CAP_SIZE);
221924edb884SFrançois Tigeot 		if (sret != DP_RECEIVER_CAP_SIZE) {
222024edb884SFrançois Tigeot 			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
222124edb884SFrançois Tigeot 			ret = -1;
222224edb884SFrançois Tigeot 			goto out_unlock;
222324edb884SFrançois Tigeot 		}
222424edb884SFrançois Tigeot 
222524edb884SFrançois Tigeot 		ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
222624edb884SFrançois Tigeot 					 DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
222724edb884SFrançois Tigeot 		if (ret < 0) {
222824edb884SFrançois Tigeot 			DRM_DEBUG_KMS("mst write failed - undocked during suspend?\n");
222924edb884SFrançois Tigeot 			ret = -1;
223024edb884SFrançois Tigeot 			goto out_unlock;
223124edb884SFrançois Tigeot 		}
2232c0e85e96SFrançois Tigeot 
2233c0e85e96SFrançois Tigeot 		/* Some hubs forget their guids after they resume */
2234c0e85e96SFrançois Tigeot 		sret = drm_dp_dpcd_read(mgr->aux, DP_GUID, guid, 16);
2235c0e85e96SFrançois Tigeot 		if (sret != 16) {
2236c0e85e96SFrançois Tigeot 			DRM_DEBUG_KMS("dpcd read failed - undocked during suspend?\n");
2237c0e85e96SFrançois Tigeot 			ret = -1;
2238c0e85e96SFrançois Tigeot 			goto out_unlock;
2239c0e85e96SFrançois Tigeot 		}
2240c0e85e96SFrançois Tigeot 		drm_dp_check_mstb_guid(mgr->mst_primary, guid);
2241c0e85e96SFrançois Tigeot 
224224edb884SFrançois Tigeot 		ret = 0;
224324edb884SFrançois Tigeot 	} else
224424edb884SFrançois Tigeot 		ret = -1;
224524edb884SFrançois Tigeot 
224624edb884SFrançois Tigeot out_unlock:
224724edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
224824edb884SFrançois Tigeot 	return ret;
224924edb884SFrançois Tigeot }
225024edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_topology_mgr_resume);
225124edb884SFrançois Tigeot 
drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr * mgr,bool up)2252a85cb24fSFrançois Tigeot static bool drm_dp_get_one_sb_msg(struct drm_dp_mst_topology_mgr *mgr, bool up)
225324edb884SFrançois Tigeot {
225424edb884SFrançois Tigeot 	int len;
225524edb884SFrançois Tigeot 	u8 replyblock[32];
225624edb884SFrançois Tigeot 	int replylen, origlen, curreply;
225724edb884SFrançois Tigeot 	int ret;
225824edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_rx *msg;
225924edb884SFrançois Tigeot 	int basereg = up ? DP_SIDEBAND_MSG_UP_REQ_BASE : DP_SIDEBAND_MSG_DOWN_REP_BASE;
226024edb884SFrançois Tigeot 	msg = up ? &mgr->up_req_recv : &mgr->down_rep_recv;
226124edb884SFrançois Tigeot 
226224edb884SFrançois Tigeot 	len = min(mgr->max_dpcd_transaction_bytes, 16);
226324edb884SFrançois Tigeot 	ret = drm_dp_dpcd_read(mgr->aux, basereg,
226424edb884SFrançois Tigeot 			       replyblock, len);
226524edb884SFrançois Tigeot 	if (ret != len) {
226624edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to read DPCD down rep %d %d\n", len, ret);
2267a85cb24fSFrançois Tigeot 		return false;
226824edb884SFrançois Tigeot 	}
226924edb884SFrançois Tigeot 	ret = drm_dp_sideband_msg_build(msg, replyblock, len, true);
227024edb884SFrançois Tigeot 	if (!ret) {
227124edb884SFrançois Tigeot 		DRM_DEBUG_KMS("sideband msg build failed %d\n", replyblock[0]);
2272a85cb24fSFrançois Tigeot 		return false;
227324edb884SFrançois Tigeot 	}
227424edb884SFrançois Tigeot 	replylen = msg->curchunk_len + msg->curchunk_hdrlen;
227524edb884SFrançois Tigeot 
227624edb884SFrançois Tigeot 	origlen = replylen;
227724edb884SFrançois Tigeot 	replylen -= len;
227824edb884SFrançois Tigeot 	curreply = len;
227924edb884SFrançois Tigeot 	while (replylen > 0) {
228024edb884SFrançois Tigeot 		len = min3(replylen, mgr->max_dpcd_transaction_bytes, 16);
228124edb884SFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, basereg + curreply,
228224edb884SFrançois Tigeot 				    replyblock, len);
228324edb884SFrançois Tigeot 		if (ret != len) {
2284a85cb24fSFrançois Tigeot 			DRM_DEBUG_KMS("failed to read a chunk (len %d, ret %d)\n",
2285a85cb24fSFrançois Tigeot 				      len, ret);
2286a85cb24fSFrançois Tigeot 			return false;
228724edb884SFrançois Tigeot 		}
2288a85cb24fSFrançois Tigeot 
228924edb884SFrançois Tigeot 		ret = drm_dp_sideband_msg_build(msg, replyblock, len, false);
2290a85cb24fSFrançois Tigeot 		if (!ret) {
229124edb884SFrançois Tigeot 			DRM_DEBUG_KMS("failed to build sideband msg\n");
2292a85cb24fSFrançois Tigeot 			return false;
2293a85cb24fSFrançois Tigeot 		}
2294a85cb24fSFrançois Tigeot 
229524edb884SFrançois Tigeot 		curreply += len;
229624edb884SFrançois Tigeot 		replylen -= len;
229724edb884SFrançois Tigeot 	}
2298a85cb24fSFrançois Tigeot 	return true;
229924edb884SFrançois Tigeot }
230024edb884SFrançois Tigeot 
drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr * mgr)230124edb884SFrançois Tigeot static int drm_dp_mst_handle_down_rep(struct drm_dp_mst_topology_mgr *mgr)
230224edb884SFrançois Tigeot {
230324edb884SFrançois Tigeot 	int ret = 0;
230424edb884SFrançois Tigeot 
2305a85cb24fSFrançois Tigeot 	if (!drm_dp_get_one_sb_msg(mgr, false)) {
2306a85cb24fSFrançois Tigeot 		memset(&mgr->down_rep_recv, 0,
2307a85cb24fSFrançois Tigeot 		       sizeof(struct drm_dp_sideband_msg_rx));
2308a85cb24fSFrançois Tigeot 		return 0;
2309a85cb24fSFrançois Tigeot 	}
231024edb884SFrançois Tigeot 
231124edb884SFrançois Tigeot 	if (mgr->down_rep_recv.have_eomt) {
231224edb884SFrançois Tigeot 		struct drm_dp_sideband_msg_tx *txmsg;
231324edb884SFrançois Tigeot 		struct drm_dp_mst_branch *mstb;
231424edb884SFrançois Tigeot 		int slot = -1;
231524edb884SFrançois Tigeot 		mstb = drm_dp_get_mst_branch_device(mgr,
231624edb884SFrançois Tigeot 						    mgr->down_rep_recv.initial_hdr.lct,
231724edb884SFrançois Tigeot 						    mgr->down_rep_recv.initial_hdr.rad);
231824edb884SFrançois Tigeot 
231924edb884SFrançois Tigeot 		if (!mstb) {
232024edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->down_rep_recv.initial_hdr.lct);
232124edb884SFrançois Tigeot 			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
232224edb884SFrançois Tigeot 			return 0;
232324edb884SFrançois Tigeot 		}
232424edb884SFrançois Tigeot 
232524edb884SFrançois Tigeot 		/* find the message */
232624edb884SFrançois Tigeot 		slot = mgr->down_rep_recv.initial_hdr.seqno;
232724edb884SFrançois Tigeot 		mutex_lock(&mgr->qlock);
232824edb884SFrançois Tigeot 		txmsg = mstb->tx_slots[slot];
232924edb884SFrançois Tigeot 		/* remove from slots */
233024edb884SFrançois Tigeot 		mutex_unlock(&mgr->qlock);
233124edb884SFrançois Tigeot 
233224edb884SFrançois Tigeot 		if (!txmsg) {
233324edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Got MST reply with no msg %p %d %d %02x %02x\n",
233424edb884SFrançois Tigeot 			       mstb,
233524edb884SFrançois Tigeot 			       mgr->down_rep_recv.initial_hdr.seqno,
233624edb884SFrançois Tigeot 			       mgr->down_rep_recv.initial_hdr.lct,
233724edb884SFrançois Tigeot 				      mgr->down_rep_recv.initial_hdr.rad[0],
233824edb884SFrançois Tigeot 				      mgr->down_rep_recv.msg[0]);
233924edb884SFrançois Tigeot 			drm_dp_put_mst_branch_device(mstb);
234024edb884SFrançois Tigeot 			memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
234124edb884SFrançois Tigeot 			return 0;
234224edb884SFrançois Tigeot 		}
234324edb884SFrançois Tigeot 
234424edb884SFrançois Tigeot 		drm_dp_sideband_parse_reply(&mgr->down_rep_recv, &txmsg->reply);
234524edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1) {
234624edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Got NAK reply: req 0x%02x, reason 0x%02x, nak data 0x%02x\n", txmsg->reply.req_type, txmsg->reply.u.nak.reason, txmsg->reply.u.nak.nak_data);
234724edb884SFrançois Tigeot 		}
234824edb884SFrançois Tigeot 
234924edb884SFrançois Tigeot 		memset(&mgr->down_rep_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
235024edb884SFrançois Tigeot 		drm_dp_put_mst_branch_device(mstb);
235124edb884SFrançois Tigeot 
235224edb884SFrançois Tigeot 		mutex_lock(&mgr->qlock);
235324edb884SFrançois Tigeot 		txmsg->state = DRM_DP_SIDEBAND_TX_RX;
235424edb884SFrançois Tigeot 		mstb->tx_slots[slot] = NULL;
235524edb884SFrançois Tigeot 		mutex_unlock(&mgr->qlock);
235624edb884SFrançois Tigeot 
2357*3f2dd94aSFrançois Tigeot 		wake_up_all(&mgr->tx_waitq);
235824edb884SFrançois Tigeot 	}
235924edb884SFrançois Tigeot 	return ret;
236024edb884SFrançois Tigeot }
236124edb884SFrançois Tigeot 
drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr * mgr)236224edb884SFrançois Tigeot static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
236324edb884SFrançois Tigeot {
236424edb884SFrançois Tigeot 	int ret = 0;
2365a85cb24fSFrançois Tigeot 
2366a85cb24fSFrançois Tigeot 	if (!drm_dp_get_one_sb_msg(mgr, true)) {
2367a85cb24fSFrançois Tigeot 		memset(&mgr->up_req_recv, 0,
2368a85cb24fSFrançois Tigeot 		       sizeof(struct drm_dp_sideband_msg_rx));
2369a85cb24fSFrançois Tigeot 		return 0;
2370a85cb24fSFrançois Tigeot 	}
237124edb884SFrançois Tigeot 
237224edb884SFrançois Tigeot 	if (mgr->up_req_recv.have_eomt) {
237324edb884SFrançois Tigeot 		struct drm_dp_sideband_msg_req_body msg;
2374aee94f86SFrançois Tigeot 		struct drm_dp_mst_branch *mstb = NULL;
237524edb884SFrançois Tigeot 		bool seqno;
2376aee94f86SFrançois Tigeot 
2377aee94f86SFrançois Tigeot 		if (!mgr->up_req_recv.initial_hdr.broadcast) {
237824edb884SFrançois Tigeot 			mstb = drm_dp_get_mst_branch_device(mgr,
237924edb884SFrançois Tigeot 							    mgr->up_req_recv.initial_hdr.lct,
238024edb884SFrançois Tigeot 							    mgr->up_req_recv.initial_hdr.rad);
238124edb884SFrançois Tigeot 			if (!mstb) {
238224edb884SFrançois Tigeot 				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
238324edb884SFrançois Tigeot 				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
238424edb884SFrançois Tigeot 				return 0;
238524edb884SFrançois Tigeot 			}
2386aee94f86SFrançois Tigeot 		}
238724edb884SFrançois Tigeot 
238824edb884SFrançois Tigeot 		seqno = mgr->up_req_recv.initial_hdr.seqno;
238924edb884SFrançois Tigeot 		drm_dp_sideband_parse_req(&mgr->up_req_recv, &msg);
239024edb884SFrançois Tigeot 
239124edb884SFrançois Tigeot 		if (msg.req_type == DP_CONNECTION_STATUS_NOTIFY) {
2392aee94f86SFrançois Tigeot 			drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
2393aee94f86SFrançois Tigeot 
2394aee94f86SFrançois Tigeot 			if (!mstb)
2395aee94f86SFrançois Tigeot 				mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.conn_stat.guid);
2396aee94f86SFrançois Tigeot 
2397aee94f86SFrançois Tigeot 			if (!mstb) {
2398aee94f86SFrançois Tigeot 				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
2399aee94f86SFrançois Tigeot 				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
2400aee94f86SFrançois Tigeot 				return 0;
2401aee94f86SFrançois Tigeot 			}
2402aee94f86SFrançois Tigeot 
240324edb884SFrançois Tigeot 			drm_dp_update_port(mstb, &msg.u.conn_stat);
2404aee94f86SFrançois Tigeot 
240524edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
240624edb884SFrançois Tigeot 			(*mgr->cbs->hotplug)(mgr);
240724edb884SFrançois Tigeot 
240824edb884SFrançois Tigeot 		} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
2409aee94f86SFrançois Tigeot 			drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
2410aee94f86SFrançois Tigeot 			if (!mstb)
2411aee94f86SFrançois Tigeot 				mstb = drm_dp_get_mst_branch_device_by_guid(mgr, msg.u.resource_stat.guid);
2412aee94f86SFrançois Tigeot 
2413aee94f86SFrançois Tigeot 			if (!mstb) {
2414aee94f86SFrançois Tigeot 				DRM_DEBUG_KMS("Got MST reply from unknown device %d\n", mgr->up_req_recv.initial_hdr.lct);
2415aee94f86SFrançois Tigeot 				memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
2416aee94f86SFrançois Tigeot 				return 0;
2417aee94f86SFrançois Tigeot 			}
2418aee94f86SFrançois Tigeot 
241924edb884SFrançois Tigeot 			DRM_DEBUG_KMS("Got RSN: pn: %d avail_pbn %d\n", msg.u.resource_stat.port_number, msg.u.resource_stat.available_pbn);
242024edb884SFrançois Tigeot 		}
242124edb884SFrançois Tigeot 
2422a85cb24fSFrançois Tigeot 		if (mstb)
242324edb884SFrançois Tigeot 			drm_dp_put_mst_branch_device(mstb);
2424a85cb24fSFrançois Tigeot 
242524edb884SFrançois Tigeot 		memset(&mgr->up_req_recv, 0, sizeof(struct drm_dp_sideband_msg_rx));
242624edb884SFrançois Tigeot 	}
242724edb884SFrançois Tigeot 	return ret;
242824edb884SFrançois Tigeot }
242924edb884SFrançois Tigeot 
243024edb884SFrançois Tigeot /**
243124edb884SFrançois Tigeot  * drm_dp_mst_hpd_irq() - MST hotplug IRQ notify
243224edb884SFrançois Tigeot  * @mgr: manager to notify irq for.
243324edb884SFrançois Tigeot  * @esi: 4 bytes from SINK_COUNT_ESI
2434aee94f86SFrançois Tigeot  * @handled: whether the hpd interrupt was consumed or not
243524edb884SFrançois Tigeot  *
243624edb884SFrançois Tigeot  * This should be called from the driver when it detects a short IRQ,
243724edb884SFrançois Tigeot  * along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The
243824edb884SFrançois Tigeot  * topology manager will process the sideband messages received as a result
243924edb884SFrançois Tigeot  * of this.
244024edb884SFrançois Tigeot  */
drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr * mgr,u8 * esi,bool * handled)244124edb884SFrançois Tigeot int drm_dp_mst_hpd_irq(struct drm_dp_mst_topology_mgr *mgr, u8 *esi, bool *handled)
244224edb884SFrançois Tigeot {
244324edb884SFrançois Tigeot 	int ret = 0;
244424edb884SFrançois Tigeot 	int sc;
244524edb884SFrançois Tigeot 	*handled = false;
244624edb884SFrançois Tigeot 	sc = esi[0] & 0x3f;
244724edb884SFrançois Tigeot 
244824edb884SFrançois Tigeot 	if (sc != mgr->sink_count) {
244924edb884SFrançois Tigeot 		mgr->sink_count = sc;
245024edb884SFrançois Tigeot 		*handled = true;
245124edb884SFrançois Tigeot 	}
245224edb884SFrançois Tigeot 
245324edb884SFrançois Tigeot 	if (esi[1] & DP_DOWN_REP_MSG_RDY) {
245424edb884SFrançois Tigeot 		ret = drm_dp_mst_handle_down_rep(mgr);
245524edb884SFrançois Tigeot 		*handled = true;
245624edb884SFrançois Tigeot 	}
245724edb884SFrançois Tigeot 
245824edb884SFrançois Tigeot 	if (esi[1] & DP_UP_REQ_MSG_RDY) {
245924edb884SFrançois Tigeot 		ret |= drm_dp_mst_handle_up_req(mgr);
246024edb884SFrançois Tigeot 		*handled = true;
246124edb884SFrançois Tigeot 	}
246224edb884SFrançois Tigeot 
246324edb884SFrançois Tigeot 	drm_dp_mst_kick_tx(mgr);
246424edb884SFrançois Tigeot 	return ret;
246524edb884SFrançois Tigeot }
246624edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_hpd_irq);
246724edb884SFrançois Tigeot 
246824edb884SFrançois Tigeot /**
246924edb884SFrançois Tigeot  * drm_dp_mst_detect_port() - get connection status for an MST port
24701dedbd3bSFrançois Tigeot  * @connector: DRM connector for this port
247124edb884SFrançois Tigeot  * @mgr: manager for this port
247224edb884SFrançois Tigeot  * @port: unverified pointer to a port
247324edb884SFrançois Tigeot  *
247424edb884SFrançois Tigeot  * This returns the current connection state for a port. It validates the
247524edb884SFrançois Tigeot  * port pointer still exists so the caller doesn't require a reference
247624edb884SFrançois Tigeot  */
drm_dp_mst_detect_port(struct drm_connector * connector,struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)24772c9916cdSFrançois Tigeot enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector,
24782c9916cdSFrançois Tigeot 						 struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
247924edb884SFrançois Tigeot {
248024edb884SFrançois Tigeot 	enum drm_connector_status status = connector_status_disconnected;
248124edb884SFrançois Tigeot 
248224edb884SFrançois Tigeot 	/* we need to search for the port in the mgr in case its gone */
248324edb884SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
248424edb884SFrançois Tigeot 	if (!port)
248524edb884SFrançois Tigeot 		return connector_status_disconnected;
248624edb884SFrançois Tigeot 
248724edb884SFrançois Tigeot 	if (!port->ddps)
248824edb884SFrançois Tigeot 		goto out;
248924edb884SFrançois Tigeot 
249024edb884SFrançois Tigeot 	switch (port->pdt) {
249124edb884SFrançois Tigeot 	case DP_PEER_DEVICE_NONE:
249224edb884SFrançois Tigeot 	case DP_PEER_DEVICE_MST_BRANCHING:
249324edb884SFrançois Tigeot 		break;
249424edb884SFrançois Tigeot 
249524edb884SFrançois Tigeot 	case DP_PEER_DEVICE_SST_SINK:
249624edb884SFrançois Tigeot 		status = connector_status_connected;
24972c9916cdSFrançois Tigeot 		/* for logical ports - cache the EDID */
24982c9916cdSFrançois Tigeot 		if (port->port_num >= 8 && !port->cached_edid) {
24992c9916cdSFrançois Tigeot 			port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
25002c9916cdSFrançois Tigeot 		}
250124edb884SFrançois Tigeot 		break;
250224edb884SFrançois Tigeot 	case DP_PEER_DEVICE_DP_LEGACY_CONV:
250324edb884SFrançois Tigeot 		if (port->ldps)
250424edb884SFrançois Tigeot 			status = connector_status_connected;
250524edb884SFrançois Tigeot 		break;
250624edb884SFrançois Tigeot 	}
250724edb884SFrançois Tigeot out:
250824edb884SFrançois Tigeot 	drm_dp_put_port(port);
250924edb884SFrançois Tigeot 	return status;
251024edb884SFrançois Tigeot }
251124edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_detect_port);
251224edb884SFrançois Tigeot 
251324edb884SFrançois Tigeot /**
2514aee94f86SFrançois Tigeot  * drm_dp_mst_port_has_audio() - Check whether port has audio capability or not
2515aee94f86SFrançois Tigeot  * @mgr: manager for this port
2516aee94f86SFrançois Tigeot  * @port: unverified pointer to a port.
2517aee94f86SFrançois Tigeot  *
2518aee94f86SFrançois Tigeot  * This returns whether the port supports audio or not.
2519aee94f86SFrançois Tigeot  */
drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)2520aee94f86SFrançois Tigeot bool drm_dp_mst_port_has_audio(struct drm_dp_mst_topology_mgr *mgr,
2521aee94f86SFrançois Tigeot 					struct drm_dp_mst_port *port)
2522aee94f86SFrançois Tigeot {
2523aee94f86SFrançois Tigeot 	bool ret = false;
2524aee94f86SFrançois Tigeot 
2525aee94f86SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
2526aee94f86SFrançois Tigeot 	if (!port)
2527aee94f86SFrançois Tigeot 		return ret;
2528aee94f86SFrançois Tigeot 	ret = port->has_audio;
2529aee94f86SFrançois Tigeot 	drm_dp_put_port(port);
2530aee94f86SFrançois Tigeot 	return ret;
2531aee94f86SFrançois Tigeot }
2532aee94f86SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_port_has_audio);
2533aee94f86SFrançois Tigeot 
2534aee94f86SFrançois Tigeot /**
253524edb884SFrançois Tigeot  * drm_dp_mst_get_edid() - get EDID for an MST port
253624edb884SFrançois Tigeot  * @connector: toplevel connector to get EDID for
253724edb884SFrançois Tigeot  * @mgr: manager for this port
253824edb884SFrançois Tigeot  * @port: unverified pointer to a port.
253924edb884SFrançois Tigeot  *
254024edb884SFrançois Tigeot  * This returns an EDID for the port connected to a connector,
254124edb884SFrançois Tigeot  * It validates the pointer still exists so the caller doesn't require a
254224edb884SFrançois Tigeot  * reference.
254324edb884SFrançois Tigeot  */
drm_dp_mst_get_edid(struct drm_connector * connector,struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)254424edb884SFrançois Tigeot struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
254524edb884SFrançois Tigeot {
254624edb884SFrançois Tigeot 	struct edid *edid = NULL;
254724edb884SFrançois Tigeot 
254824edb884SFrançois Tigeot 	/* we need to search for the port in the mgr in case its gone */
254924edb884SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
255024edb884SFrançois Tigeot 	if (!port)
255124edb884SFrançois Tigeot 		return NULL;
255224edb884SFrançois Tigeot 
25532c9916cdSFrançois Tigeot 	if (port->cached_edid)
25542c9916cdSFrançois Tigeot 		edid = drm_edid_duplicate(port->cached_edid);
2555a05eeebfSFrançois Tigeot 	else {
255624edb884SFrançois Tigeot 		edid = drm_get_edid(connector, &port->aux.ddc);
25572c9916cdSFrançois Tigeot 		drm_mode_connector_set_tile_property(connector);
2558a05eeebfSFrançois Tigeot 	}
2559aee94f86SFrançois Tigeot 	port->has_audio = drm_detect_monitor_audio(edid);
256024edb884SFrançois Tigeot 	drm_dp_put_port(port);
256124edb884SFrançois Tigeot 	return edid;
256224edb884SFrançois Tigeot }
256324edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_get_edid);
256424edb884SFrançois Tigeot 
256524edb884SFrançois Tigeot /**
256624edb884SFrançois Tigeot  * drm_dp_find_vcpi_slots() - find slots for this PBN value
256724edb884SFrançois Tigeot  * @mgr: manager to use
256824edb884SFrançois Tigeot  * @pbn: payload bandwidth to convert into slots.
256924edb884SFrançois Tigeot  */
drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr * mgr,int pbn)257024edb884SFrançois Tigeot int drm_dp_find_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr,
257124edb884SFrançois Tigeot 			   int pbn)
257224edb884SFrançois Tigeot {
257324edb884SFrançois Tigeot 	int num_slots;
257424edb884SFrançois Tigeot 
257524edb884SFrançois Tigeot 	num_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
257624edb884SFrançois Tigeot 
2577a85cb24fSFrançois Tigeot 	/* max. time slots - one slot for MTP header */
2578a85cb24fSFrançois Tigeot 	if (num_slots > 63)
257924edb884SFrançois Tigeot 		return -ENOSPC;
258024edb884SFrançois Tigeot 	return num_slots;
258124edb884SFrançois Tigeot }
258224edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_find_vcpi_slots);
258324edb884SFrançois Tigeot 
drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_vcpi * vcpi,int pbn,int slots)258424edb884SFrançois Tigeot static int drm_dp_init_vcpi(struct drm_dp_mst_topology_mgr *mgr,
2585a85cb24fSFrançois Tigeot 			    struct drm_dp_vcpi *vcpi, int pbn, int slots)
258624edb884SFrançois Tigeot {
258724edb884SFrançois Tigeot 	int ret;
258824edb884SFrançois Tigeot 
2589a85cb24fSFrançois Tigeot 	/* max. time slots - one slot for MTP header */
2590a85cb24fSFrançois Tigeot 	if (slots > 63)
259124edb884SFrançois Tigeot 		return -ENOSPC;
259224edb884SFrançois Tigeot 
259324edb884SFrançois Tigeot 	vcpi->pbn = pbn;
2594a85cb24fSFrançois Tigeot 	vcpi->aligned_pbn = slots * mgr->pbn_div;
2595a85cb24fSFrançois Tigeot 	vcpi->num_slots = slots;
259624edb884SFrançois Tigeot 
259724edb884SFrançois Tigeot 	ret = drm_dp_mst_assign_payload_id(mgr, vcpi);
259824edb884SFrançois Tigeot 	if (ret < 0)
259924edb884SFrançois Tigeot 		return ret;
260024edb884SFrançois Tigeot 	return 0;
260124edb884SFrançois Tigeot }
260224edb884SFrançois Tigeot 
260324edb884SFrançois Tigeot /**
2604*3f2dd94aSFrançois Tigeot  * drm_dp_atomic_find_vcpi_slots() - Find and add vcpi slots to the state
2605*3f2dd94aSFrançois Tigeot  * @state: global atomic state
2606*3f2dd94aSFrançois Tigeot  * @mgr: MST topology manager for the port
2607*3f2dd94aSFrançois Tigeot  * @port: port to find vcpi slots for
2608*3f2dd94aSFrançois Tigeot  * @pbn: bandwidth required for the mode in PBN
2609*3f2dd94aSFrançois Tigeot  *
2610*3f2dd94aSFrançois Tigeot  * RETURNS:
2611*3f2dd94aSFrançois Tigeot  * Total slots in the atomic state assigned for this port or error
2612*3f2dd94aSFrançois Tigeot  */
drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state * state,struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int pbn)2613*3f2dd94aSFrançois Tigeot int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
2614*3f2dd94aSFrançois Tigeot 				  struct drm_dp_mst_topology_mgr *mgr,
2615*3f2dd94aSFrançois Tigeot 				  struct drm_dp_mst_port *port, int pbn)
2616*3f2dd94aSFrançois Tigeot {
2617*3f2dd94aSFrançois Tigeot 	struct drm_dp_mst_topology_state *topology_state;
2618*3f2dd94aSFrançois Tigeot 	int req_slots;
2619*3f2dd94aSFrançois Tigeot 
2620*3f2dd94aSFrançois Tigeot 	topology_state = drm_atomic_get_mst_topology_state(state, mgr);
2621*3f2dd94aSFrançois Tigeot 	if (IS_ERR(topology_state))
2622*3f2dd94aSFrançois Tigeot 		return PTR_ERR(topology_state);
2623*3f2dd94aSFrançois Tigeot 
2624*3f2dd94aSFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
2625*3f2dd94aSFrançois Tigeot 	if (port == NULL)
2626*3f2dd94aSFrançois Tigeot 		return -EINVAL;
2627*3f2dd94aSFrançois Tigeot 	req_slots = DIV_ROUND_UP(pbn, mgr->pbn_div);
2628*3f2dd94aSFrançois Tigeot 	DRM_DEBUG_KMS("vcpi slots req=%d, avail=%d\n",
2629*3f2dd94aSFrançois Tigeot 			req_slots, topology_state->avail_slots);
2630*3f2dd94aSFrançois Tigeot 
2631*3f2dd94aSFrançois Tigeot 	if (req_slots > topology_state->avail_slots) {
2632*3f2dd94aSFrançois Tigeot 		drm_dp_put_port(port);
2633*3f2dd94aSFrançois Tigeot 		return -ENOSPC;
2634*3f2dd94aSFrançois Tigeot 	}
2635*3f2dd94aSFrançois Tigeot 
2636*3f2dd94aSFrançois Tigeot 	topology_state->avail_slots -= req_slots;
2637*3f2dd94aSFrançois Tigeot 	DRM_DEBUG_KMS("vcpi slots avail=%d", topology_state->avail_slots);
2638*3f2dd94aSFrançois Tigeot 
2639*3f2dd94aSFrançois Tigeot 	drm_dp_put_port(port);
2640*3f2dd94aSFrançois Tigeot 	return req_slots;
2641*3f2dd94aSFrançois Tigeot }
2642*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_dp_atomic_find_vcpi_slots);
2643*3f2dd94aSFrançois Tigeot 
2644*3f2dd94aSFrançois Tigeot /**
2645*3f2dd94aSFrançois Tigeot  * drm_dp_atomic_release_vcpi_slots() - Release allocated vcpi slots
2646*3f2dd94aSFrançois Tigeot  * @state: global atomic state
2647*3f2dd94aSFrançois Tigeot  * @mgr: MST topology manager for the port
2648*3f2dd94aSFrançois Tigeot  * @slots: number of vcpi slots to release
2649*3f2dd94aSFrançois Tigeot  *
2650*3f2dd94aSFrançois Tigeot  * RETURNS:
2651*3f2dd94aSFrançois Tigeot  * 0 if @slots were added back to &drm_dp_mst_topology_state->avail_slots or
2652*3f2dd94aSFrançois Tigeot  * negative error code
2653*3f2dd94aSFrançois Tigeot  */
drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state * state,struct drm_dp_mst_topology_mgr * mgr,int slots)2654*3f2dd94aSFrançois Tigeot int drm_dp_atomic_release_vcpi_slots(struct drm_atomic_state *state,
2655*3f2dd94aSFrançois Tigeot 				     struct drm_dp_mst_topology_mgr *mgr,
2656*3f2dd94aSFrançois Tigeot 				     int slots)
2657*3f2dd94aSFrançois Tigeot {
2658*3f2dd94aSFrançois Tigeot 	struct drm_dp_mst_topology_state *topology_state;
2659*3f2dd94aSFrançois Tigeot 
2660*3f2dd94aSFrançois Tigeot 	topology_state = drm_atomic_get_mst_topology_state(state, mgr);
2661*3f2dd94aSFrançois Tigeot 	if (IS_ERR(topology_state))
2662*3f2dd94aSFrançois Tigeot 		return PTR_ERR(topology_state);
2663*3f2dd94aSFrançois Tigeot 
2664*3f2dd94aSFrançois Tigeot 	/* We cannot rely on port->vcpi.num_slots to update
2665*3f2dd94aSFrançois Tigeot 	 * topology_state->avail_slots as the port may not exist if the parent
2666*3f2dd94aSFrançois Tigeot 	 * branch device was unplugged. This should be fixed by tracking
2667*3f2dd94aSFrançois Tigeot 	 * per-port slot allocation in drm_dp_mst_topology_state instead of
2668*3f2dd94aSFrançois Tigeot 	 * depending on the caller to tell us how many slots to release.
2669*3f2dd94aSFrançois Tigeot 	 */
2670*3f2dd94aSFrançois Tigeot 	topology_state->avail_slots += slots;
2671*3f2dd94aSFrançois Tigeot 	DRM_DEBUG_KMS("vcpi slots released=%d, avail=%d\n",
2672*3f2dd94aSFrançois Tigeot 			slots, topology_state->avail_slots);
2673*3f2dd94aSFrançois Tigeot 
2674*3f2dd94aSFrançois Tigeot 	return 0;
2675*3f2dd94aSFrançois Tigeot }
2676*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_dp_atomic_release_vcpi_slots);
2677*3f2dd94aSFrançois Tigeot 
2678*3f2dd94aSFrançois Tigeot /**
267924edb884SFrançois Tigeot  * drm_dp_mst_allocate_vcpi() - Allocate a virtual channel
268024edb884SFrançois Tigeot  * @mgr: manager for this port
268124edb884SFrançois Tigeot  * @port: port to allocate a virtual channel for.
268224edb884SFrançois Tigeot  * @pbn: payload bandwidth number to request
268324edb884SFrançois Tigeot  * @slots: returned number of slots for this PBN.
268424edb884SFrançois Tigeot  */
drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,int pbn,int slots)2685a85cb24fSFrançois Tigeot bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr,
2686a85cb24fSFrançois Tigeot 			      struct drm_dp_mst_port *port, int pbn, int slots)
268724edb884SFrançois Tigeot {
268824edb884SFrançois Tigeot 	int ret;
268924edb884SFrançois Tigeot 
269024edb884SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
269124edb884SFrançois Tigeot 	if (!port)
269224edb884SFrançois Tigeot 		return false;
269324edb884SFrançois Tigeot 
2694a85cb24fSFrançois Tigeot 	if (slots < 0)
2695a85cb24fSFrançois Tigeot 		return false;
2696a85cb24fSFrançois Tigeot 
269724edb884SFrançois Tigeot 	if (port->vcpi.vcpi > 0) {
269824edb884SFrançois Tigeot 		DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
269924edb884SFrançois Tigeot 		if (pbn == port->vcpi.pbn) {
2700aee94f86SFrançois Tigeot 			drm_dp_put_port(port);
270124edb884SFrançois Tigeot 			return true;
270224edb884SFrançois Tigeot 		}
270324edb884SFrançois Tigeot 	}
270424edb884SFrançois Tigeot 
2705a85cb24fSFrançois Tigeot 	ret = drm_dp_init_vcpi(mgr, &port->vcpi, pbn, slots);
270624edb884SFrançois Tigeot 	if (ret) {
2707a85cb24fSFrançois Tigeot 		DRM_DEBUG_KMS("failed to init vcpi slots=%d max=63 ret=%d\n",
2708a85cb24fSFrançois Tigeot 				DIV_ROUND_UP(pbn, mgr->pbn_div), ret);
270924edb884SFrançois Tigeot 		goto out;
271024edb884SFrançois Tigeot 	}
2711a85cb24fSFrançois Tigeot 	DRM_DEBUG_KMS("initing vcpi for pbn=%d slots=%d\n",
2712a85cb24fSFrançois Tigeot 			pbn, port->vcpi.num_slots);
271324edb884SFrançois Tigeot 
271424edb884SFrançois Tigeot 	drm_dp_put_port(port);
271524edb884SFrançois Tigeot 	return true;
271624edb884SFrançois Tigeot out:
271724edb884SFrançois Tigeot 	return false;
271824edb884SFrançois Tigeot }
271924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_allocate_vcpi);
272024edb884SFrançois Tigeot 
drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)2721477eb7f9SFrançois Tigeot int drm_dp_mst_get_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
2722477eb7f9SFrançois Tigeot {
2723477eb7f9SFrançois Tigeot 	int slots = 0;
2724477eb7f9SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
2725477eb7f9SFrançois Tigeot 	if (!port)
2726477eb7f9SFrançois Tigeot 		return slots;
2727477eb7f9SFrançois Tigeot 
2728477eb7f9SFrançois Tigeot 	slots = port->vcpi.num_slots;
2729477eb7f9SFrançois Tigeot 	drm_dp_put_port(port);
2730477eb7f9SFrançois Tigeot 	return slots;
2731477eb7f9SFrançois Tigeot }
2732477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_get_vcpi_slots);
2733477eb7f9SFrançois Tigeot 
273424edb884SFrançois Tigeot /**
273524edb884SFrançois Tigeot  * drm_dp_mst_reset_vcpi_slots() - Reset number of slots to 0 for VCPI
273624edb884SFrançois Tigeot  * @mgr: manager for this port
273724edb884SFrançois Tigeot  * @port: unverified pointer to a port.
273824edb884SFrançois Tigeot  *
273924edb884SFrançois Tigeot  * This just resets the number of slots for the ports VCPI for later programming.
274024edb884SFrançois Tigeot  */
drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)274124edb884SFrançois Tigeot void drm_dp_mst_reset_vcpi_slots(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
274224edb884SFrançois Tigeot {
274324edb884SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
274424edb884SFrançois Tigeot 	if (!port)
274524edb884SFrançois Tigeot 		return;
274624edb884SFrançois Tigeot 	port->vcpi.num_slots = 0;
274724edb884SFrançois Tigeot 	drm_dp_put_port(port);
274824edb884SFrançois Tigeot }
274924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_reset_vcpi_slots);
275024edb884SFrançois Tigeot 
275124edb884SFrançois Tigeot /**
275224edb884SFrançois Tigeot  * drm_dp_mst_deallocate_vcpi() - deallocate a VCPI
275324edb884SFrançois Tigeot  * @mgr: manager for this port
275424edb884SFrançois Tigeot  * @port: unverified port to deallocate vcpi for
275524edb884SFrançois Tigeot  */
drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port)275624edb884SFrançois Tigeot void drm_dp_mst_deallocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port)
275724edb884SFrançois Tigeot {
275824edb884SFrançois Tigeot 	port = drm_dp_get_validated_port_ref(mgr, port);
275924edb884SFrançois Tigeot 	if (!port)
276024edb884SFrançois Tigeot 		return;
276124edb884SFrançois Tigeot 
276224edb884SFrançois Tigeot 	drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
276324edb884SFrançois Tigeot 	port->vcpi.num_slots = 0;
276424edb884SFrançois Tigeot 	port->vcpi.pbn = 0;
276524edb884SFrançois Tigeot 	port->vcpi.aligned_pbn = 0;
276624edb884SFrançois Tigeot 	port->vcpi.vcpi = 0;
276724edb884SFrançois Tigeot 	drm_dp_put_port(port);
276824edb884SFrançois Tigeot }
276924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_deallocate_vcpi);
277024edb884SFrançois Tigeot 
drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr * mgr,int id,struct drm_dp_payload * payload)277124edb884SFrançois Tigeot static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
277224edb884SFrançois Tigeot 				     int id, struct drm_dp_payload *payload)
277324edb884SFrançois Tigeot {
277424edb884SFrançois Tigeot 	u8 payload_alloc[3], status;
277524edb884SFrançois Tigeot 	int ret;
277624edb884SFrançois Tigeot 	int retries = 0;
277724edb884SFrançois Tigeot 
277824edb884SFrançois Tigeot 	drm_dp_dpcd_writeb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS,
277924edb884SFrançois Tigeot 			   DP_PAYLOAD_TABLE_UPDATED);
278024edb884SFrançois Tigeot 
278124edb884SFrançois Tigeot 	payload_alloc[0] = id;
278224edb884SFrançois Tigeot 	payload_alloc[1] = payload->start_slot;
278324edb884SFrançois Tigeot 	payload_alloc[2] = payload->num_slots;
278424edb884SFrançois Tigeot 
278524edb884SFrançois Tigeot 	ret = drm_dp_dpcd_write(mgr->aux, DP_PAYLOAD_ALLOCATE_SET, payload_alloc, 3);
278624edb884SFrançois Tigeot 	if (ret != 3) {
278724edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to write payload allocation %d\n", ret);
278824edb884SFrançois Tigeot 		goto fail;
278924edb884SFrançois Tigeot 	}
279024edb884SFrançois Tigeot 
279124edb884SFrançois Tigeot retry:
279224edb884SFrançois Tigeot 	ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
279324edb884SFrançois Tigeot 	if (ret < 0) {
279424edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
279524edb884SFrançois Tigeot 		goto fail;
279624edb884SFrançois Tigeot 	}
279724edb884SFrançois Tigeot 
279824edb884SFrançois Tigeot 	if (!(status & DP_PAYLOAD_TABLE_UPDATED)) {
279924edb884SFrançois Tigeot 		retries++;
280024edb884SFrançois Tigeot 		if (retries < 20) {
280124edb884SFrançois Tigeot 			usleep_range(10000, 20000);
280224edb884SFrançois Tigeot 			goto retry;
280324edb884SFrançois Tigeot 		}
280424edb884SFrançois Tigeot 		DRM_DEBUG_KMS("status not set after read payload table status %d\n", status);
280524edb884SFrançois Tigeot 		ret = -EINVAL;
280624edb884SFrançois Tigeot 		goto fail;
280724edb884SFrançois Tigeot 	}
280824edb884SFrançois Tigeot 	ret = 0;
280924edb884SFrançois Tigeot fail:
281024edb884SFrançois Tigeot 	return ret;
281124edb884SFrançois Tigeot }
281224edb884SFrançois Tigeot 
281324edb884SFrançois Tigeot 
281424edb884SFrançois Tigeot /**
281524edb884SFrançois Tigeot  * drm_dp_check_act_status() - Check ACT handled status.
281624edb884SFrançois Tigeot  * @mgr: manager to use
281724edb884SFrançois Tigeot  *
281824edb884SFrançois Tigeot  * Check the payload status bits in the DPCD for ACT handled completion.
281924edb884SFrançois Tigeot  */
drm_dp_check_act_status(struct drm_dp_mst_topology_mgr * mgr)282024edb884SFrançois Tigeot int drm_dp_check_act_status(struct drm_dp_mst_topology_mgr *mgr)
282124edb884SFrançois Tigeot {
282224edb884SFrançois Tigeot 	u8 status;
282324edb884SFrançois Tigeot 	int ret;
282424edb884SFrançois Tigeot 	int count = 0;
282524edb884SFrançois Tigeot 
282624edb884SFrançois Tigeot 	do {
282724edb884SFrançois Tigeot 		ret = drm_dp_dpcd_readb(mgr->aux, DP_PAYLOAD_TABLE_UPDATE_STATUS, &status);
282824edb884SFrançois Tigeot 
282924edb884SFrançois Tigeot 		if (ret < 0) {
283024edb884SFrançois Tigeot 			DRM_DEBUG_KMS("failed to read payload table status %d\n", ret);
283124edb884SFrançois Tigeot 			goto fail;
283224edb884SFrançois Tigeot 		}
283324edb884SFrançois Tigeot 
283424edb884SFrançois Tigeot 		if (status & DP_PAYLOAD_ACT_HANDLED)
283524edb884SFrançois Tigeot 			break;
283624edb884SFrançois Tigeot 		count++;
283724edb884SFrançois Tigeot 		udelay(100);
283824edb884SFrançois Tigeot 
283924edb884SFrançois Tigeot 	} while (count < 30);
284024edb884SFrançois Tigeot 
284124edb884SFrançois Tigeot 	if (!(status & DP_PAYLOAD_ACT_HANDLED)) {
284224edb884SFrançois Tigeot 		DRM_DEBUG_KMS("failed to get ACT bit %d after %d retries\n", status, count);
284324edb884SFrançois Tigeot 		ret = -EINVAL;
284424edb884SFrançois Tigeot 		goto fail;
284524edb884SFrançois Tigeot 	}
284624edb884SFrançois Tigeot 	return 0;
284724edb884SFrançois Tigeot fail:
284824edb884SFrançois Tigeot 	return ret;
284924edb884SFrançois Tigeot }
285024edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_check_act_status);
285124edb884SFrançois Tigeot 
285224edb884SFrançois Tigeot /**
285324edb884SFrançois Tigeot  * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
285424edb884SFrançois Tigeot  * @clock: dot clock for the mode
285524edb884SFrançois Tigeot  * @bpp: bpp for the mode.
285624edb884SFrançois Tigeot  *
285724edb884SFrançois Tigeot  * This uses the formula in the spec to calculate the PBN value for a mode.
285824edb884SFrançois Tigeot  */
drm_dp_calc_pbn_mode(int clock,int bpp)285924edb884SFrançois Tigeot int drm_dp_calc_pbn_mode(int clock, int bpp)
286024edb884SFrançois Tigeot {
2861aee94f86SFrançois Tigeot 	u64 kbps;
2862aee94f86SFrançois Tigeot 	s64 peak_kbps;
2863aee94f86SFrançois Tigeot 	u32 numerator;
2864aee94f86SFrançois Tigeot 	u32 denominator;
286524edb884SFrançois Tigeot 
2866aee94f86SFrançois Tigeot 	kbps = clock * bpp;
286724edb884SFrançois Tigeot 
2868aee94f86SFrançois Tigeot 	/*
2869aee94f86SFrançois Tigeot 	 * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
2870aee94f86SFrançois Tigeot 	 * The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
2871aee94f86SFrançois Tigeot 	 * common multiplier to render an integer PBN for all link rate/lane
2872aee94f86SFrançois Tigeot 	 * counts combinations
2873aee94f86SFrançois Tigeot 	 * calculate
2874aee94f86SFrançois Tigeot 	 * peak_kbps *= (1006/1000)
2875aee94f86SFrançois Tigeot 	 * peak_kbps *= (64/54)
2876aee94f86SFrançois Tigeot 	 * peak_kbps *= 8    convert to bytes
2877aee94f86SFrançois Tigeot 	 */
287824edb884SFrançois Tigeot 
2879aee94f86SFrançois Tigeot 	numerator = 64 * 1006;
2880aee94f86SFrançois Tigeot 	denominator = 54 * 8 * 1000 * 1000;
288124edb884SFrançois Tigeot 
2882aee94f86SFrançois Tigeot 	kbps *= numerator;
2883aee94f86SFrançois Tigeot 	peak_kbps = drm_fixp_from_fraction(kbps, denominator);
2884aee94f86SFrançois Tigeot 
2885aee94f86SFrançois Tigeot 	return drm_fixp2int_ceil(peak_kbps);
288624edb884SFrançois Tigeot }
288724edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
288824edb884SFrançois Tigeot 
test_calc_pbn_mode(void)288924edb884SFrançois Tigeot static int test_calc_pbn_mode(void)
289024edb884SFrançois Tigeot {
289124edb884SFrançois Tigeot 	int ret;
289224edb884SFrançois Tigeot 	ret = drm_dp_calc_pbn_mode(154000, 30);
2893aee94f86SFrançois Tigeot 	if (ret != 689) {
2894aee94f86SFrançois Tigeot 		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
2895aee94f86SFrançois Tigeot 				154000, 30, 689, ret);
289624edb884SFrançois Tigeot 		return -EINVAL;
2897aee94f86SFrançois Tigeot 	}
289824edb884SFrançois Tigeot 	ret = drm_dp_calc_pbn_mode(234000, 30);
2899aee94f86SFrançois Tigeot 	if (ret != 1047) {
2900aee94f86SFrançois Tigeot 		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
2901aee94f86SFrançois Tigeot 				234000, 30, 1047, ret);
290224edb884SFrançois Tigeot 		return -EINVAL;
2903aee94f86SFrançois Tigeot 	}
2904aee94f86SFrançois Tigeot 	ret = drm_dp_calc_pbn_mode(297000, 24);
2905aee94f86SFrançois Tigeot 	if (ret != 1063) {
2906aee94f86SFrançois Tigeot 		DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
2907aee94f86SFrançois Tigeot 				297000, 24, 1063, ret);
2908aee94f86SFrançois Tigeot 		return -EINVAL;
2909aee94f86SFrançois Tigeot 	}
291024edb884SFrançois Tigeot 	return 0;
291124edb884SFrançois Tigeot }
291224edb884SFrançois Tigeot 
291324edb884SFrançois Tigeot /* we want to kick the TX after we've ack the up/down IRQs. */
drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr * mgr)291424edb884SFrançois Tigeot static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr)
291524edb884SFrançois Tigeot {
291624edb884SFrançois Tigeot 	queue_work(system_long_wq, &mgr->tx_work);
291724edb884SFrançois Tigeot }
291824edb884SFrançois Tigeot 
drm_dp_mst_dump_mstb(struct seq_file * m,struct drm_dp_mst_branch * mstb)291924edb884SFrançois Tigeot static void drm_dp_mst_dump_mstb(struct seq_file *m,
292024edb884SFrançois Tigeot 				 struct drm_dp_mst_branch *mstb)
292124edb884SFrançois Tigeot {
292224edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
292324edb884SFrançois Tigeot 	int tabs = mstb->lct;
292424edb884SFrançois Tigeot 	char prefix[10];
292524edb884SFrançois Tigeot 	int i;
292624edb884SFrançois Tigeot 
292724edb884SFrançois Tigeot 	for (i = 0; i < tabs; i++)
292824edb884SFrançois Tigeot 		prefix[i] = '\t';
292924edb884SFrançois Tigeot 	prefix[i] = '\0';
293024edb884SFrançois Tigeot 
293124edb884SFrançois Tigeot 	seq_printf(m, "%smst: %p, %d\n", prefix, mstb, mstb->num_ports);
293224edb884SFrançois Tigeot 	list_for_each_entry(port, &mstb->ports, next) {
29338621f407SFrançois Tigeot 		seq_printf(m, "%sport: %d: input: %d: pdt: %d, ddps: %d ldps: %d, sdp: %d/%d, %p, conn: %p\n", prefix, port->port_num, port->input, port->pdt, port->ddps, port->ldps, port->num_sdp_streams, port->num_sdp_stream_sinks, port, port->connector);
293424edb884SFrançois Tigeot 		if (port->mstb)
293524edb884SFrançois Tigeot 			drm_dp_mst_dump_mstb(m, port->mstb);
293624edb884SFrançois Tigeot 	}
293724edb884SFrançois Tigeot }
293824edb884SFrançois Tigeot 
dump_dp_payload_table(struct drm_dp_mst_topology_mgr * mgr,char * buf)293924edb884SFrançois Tigeot static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
294024edb884SFrançois Tigeot 				  char *buf)
294124edb884SFrançois Tigeot {
294224edb884SFrançois Tigeot 	int i;
2943*3f2dd94aSFrançois Tigeot 
2944*3f2dd94aSFrançois Tigeot 	for (i = 0; i < 64; i += 16) {
2945*3f2dd94aSFrançois Tigeot 		if (drm_dp_dpcd_read(mgr->aux,
2946*3f2dd94aSFrançois Tigeot 				     DP_PAYLOAD_TABLE_UPDATE_STATUS + i,
2947*3f2dd94aSFrançois Tigeot 				     &buf[i], 16) != 16)
294824edb884SFrançois Tigeot 			return false;
294924edb884SFrançois Tigeot 	}
2950*3f2dd94aSFrançois Tigeot 	return true;
2951*3f2dd94aSFrançois Tigeot }
295224edb884SFrançois Tigeot 
fetch_monitor_name(struct drm_dp_mst_topology_mgr * mgr,struct drm_dp_mst_port * port,char * name,int namelen)29538621f407SFrançois Tigeot static void fetch_monitor_name(struct drm_dp_mst_topology_mgr *mgr,
29548621f407SFrançois Tigeot 			       struct drm_dp_mst_port *port, char *name,
29558621f407SFrançois Tigeot 			       int namelen)
29568621f407SFrançois Tigeot {
29578621f407SFrançois Tigeot 	struct edid *mst_edid;
29588621f407SFrançois Tigeot 
29598621f407SFrançois Tigeot 	mst_edid = drm_dp_mst_get_edid(port->connector, mgr, port);
29608621f407SFrançois Tigeot 	drm_edid_get_monitor_name(mst_edid, name, namelen);
29618621f407SFrançois Tigeot }
29628621f407SFrançois Tigeot 
296324edb884SFrançois Tigeot /**
296424edb884SFrançois Tigeot  * drm_dp_mst_dump_topology(): dump topology to seq file.
296524edb884SFrançois Tigeot  * @m: seq_file to dump output to
296624edb884SFrançois Tigeot  * @mgr: manager to dump current topology for.
296724edb884SFrançois Tigeot  *
296824edb884SFrançois Tigeot  * helper to dump MST topology to a seq file for debugfs.
296924edb884SFrançois Tigeot  */
drm_dp_mst_dump_topology(struct seq_file * m,struct drm_dp_mst_topology_mgr * mgr)297024edb884SFrançois Tigeot void drm_dp_mst_dump_topology(struct seq_file *m,
297124edb884SFrançois Tigeot 			      struct drm_dp_mst_topology_mgr *mgr)
297224edb884SFrançois Tigeot {
297324edb884SFrançois Tigeot 	int i;
297424edb884SFrançois Tigeot 	struct drm_dp_mst_port *port;
29758621f407SFrançois Tigeot 
297624edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
297724edb884SFrançois Tigeot 	if (mgr->mst_primary)
297824edb884SFrançois Tigeot 		drm_dp_mst_dump_mstb(m, mgr->mst_primary);
297924edb884SFrançois Tigeot 
298024edb884SFrançois Tigeot 	/* dump VCPIs */
298124edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
298224edb884SFrançois Tigeot 
298324edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
29848621f407SFrançois Tigeot 	seq_printf(m, "vcpi: %lx %lx %d\n", mgr->payload_mask, mgr->vcpi_mask,
29858621f407SFrançois Tigeot 		mgr->max_payloads);
298624edb884SFrançois Tigeot 
298724edb884SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
298824edb884SFrançois Tigeot 		if (mgr->proposed_vcpis[i]) {
29898621f407SFrançois Tigeot 			char name[14];
29908621f407SFrançois Tigeot 
299124edb884SFrançois Tigeot 			port = container_of(mgr->proposed_vcpis[i], struct drm_dp_mst_port, vcpi);
29928621f407SFrançois Tigeot 			fetch_monitor_name(mgr, port, name, sizeof(name));
29938621f407SFrançois Tigeot 			seq_printf(m, "vcpi %d: %d %d %d sink name: %s\n", i,
29948621f407SFrançois Tigeot 				   port->port_num, port->vcpi.vcpi,
29958621f407SFrançois Tigeot 				   port->vcpi.num_slots,
29968621f407SFrançois Tigeot 				   (*name != 0) ? name :  "Unknown");
299724edb884SFrançois Tigeot 		} else
29988621f407SFrançois Tigeot 			seq_printf(m, "vcpi %d:unused\n", i);
299924edb884SFrançois Tigeot 	}
300024edb884SFrançois Tigeot 	for (i = 0; i < mgr->max_payloads; i++) {
300124edb884SFrançois Tigeot 		seq_printf(m, "payload %d: %d, %d, %d\n",
300224edb884SFrançois Tigeot 			   i,
300324edb884SFrançois Tigeot 			   mgr->payloads[i].payload_state,
300424edb884SFrançois Tigeot 			   mgr->payloads[i].start_slot,
300524edb884SFrançois Tigeot 			   mgr->payloads[i].num_slots);
300624edb884SFrançois Tigeot 
300724edb884SFrançois Tigeot 
300824edb884SFrançois Tigeot 	}
300924edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
301024edb884SFrançois Tigeot 
301124edb884SFrançois Tigeot 	mutex_lock(&mgr->lock);
301224edb884SFrançois Tigeot 	if (mgr->mst_primary) {
301324edb884SFrançois Tigeot 		u8 buf[64];
301424edb884SFrançois Tigeot 		int ret;
3015*3f2dd94aSFrançois Tigeot 
301624edb884SFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, DP_DPCD_REV, buf, DP_RECEIVER_CAP_SIZE);
3017*3f2dd94aSFrançois Tigeot 		seq_printf(m, "dpcd: %*ph\n", DP_RECEIVER_CAP_SIZE, buf);
301824edb884SFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, DP_FAUX_CAP, buf, 2);
3019*3f2dd94aSFrançois Tigeot 		seq_printf(m, "faux/mst: %*ph\n", 2, buf);
302024edb884SFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, DP_MSTM_CTRL, buf, 1);
3021*3f2dd94aSFrançois Tigeot 		seq_printf(m, "mst ctrl: %*ph\n", 1, buf);
302224edb884SFrançois Tigeot 
3023a05eeebfSFrançois Tigeot 		/* dump the standard OUI branch header */
3024a05eeebfSFrançois Tigeot 		ret = drm_dp_dpcd_read(mgr->aux, DP_BRANCH_OUI, buf, DP_BRANCH_OUI_HEADER_SIZE);
3025*3f2dd94aSFrançois Tigeot 		seq_printf(m, "branch oui: %*phN devid: ", 3, buf);
30268621f407SFrançois Tigeot 		for (i = 0x3; i < 0x8 && buf[i]; i++)
3027a05eeebfSFrançois Tigeot 			seq_printf(m, "%c", buf[i]);
3028*3f2dd94aSFrançois Tigeot 		seq_printf(m, " revision: hw: %x.%x sw: %x.%x\n",
3029*3f2dd94aSFrançois Tigeot 			   buf[0x9] >> 4, buf[0x9] & 0xf, buf[0xa], buf[0xb]);
3030*3f2dd94aSFrançois Tigeot 		if (dump_dp_payload_table(mgr, buf))
3031*3f2dd94aSFrançois Tigeot 			seq_printf(m, "payload table: %*ph\n", 63, buf);
303224edb884SFrançois Tigeot 
303324edb884SFrançois Tigeot 	}
303424edb884SFrançois Tigeot 
303524edb884SFrançois Tigeot 	mutex_unlock(&mgr->lock);
303624edb884SFrançois Tigeot 
303724edb884SFrançois Tigeot }
303824edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_dump_topology);
303924edb884SFrançois Tigeot 
drm_dp_tx_work(struct work_struct * work)304024edb884SFrançois Tigeot static void drm_dp_tx_work(struct work_struct *work)
304124edb884SFrançois Tigeot {
304224edb884SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, tx_work);
304324edb884SFrançois Tigeot 
304424edb884SFrançois Tigeot 	mutex_lock(&mgr->qlock);
30451dedbd3bSFrançois Tigeot 	if (!list_empty(&mgr->tx_msg_downq))
304624edb884SFrançois Tigeot 		process_single_down_tx_qlock(mgr);
304724edb884SFrançois Tigeot 	mutex_unlock(&mgr->qlock);
304824edb884SFrançois Tigeot }
304924edb884SFrançois Tigeot 
drm_dp_free_mst_port(struct kref * kref)3050aee94f86SFrançois Tigeot static void drm_dp_free_mst_port(struct kref *kref)
3051aee94f86SFrançois Tigeot {
3052aee94f86SFrançois Tigeot 	struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
3053aee94f86SFrançois Tigeot 	kref_put(&port->parent->kref, drm_dp_free_mst_branch_device);
3054aee94f86SFrançois Tigeot 	kfree(port);
3055aee94f86SFrançois Tigeot }
3056aee94f86SFrançois Tigeot 
drm_dp_destroy_connector_work(struct work_struct * work)305719c468b4SFrançois Tigeot static void drm_dp_destroy_connector_work(struct work_struct *work)
305819c468b4SFrançois Tigeot {
305919c468b4SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
306019c468b4SFrançois Tigeot 	struct drm_dp_mst_port *port;
3061a05eeebfSFrançois Tigeot 	bool send_hotplug = false;
306219c468b4SFrançois Tigeot 	/*
306319c468b4SFrançois Tigeot 	 * Not a regular list traverse as we have to drop the destroy
306419c468b4SFrançois Tigeot 	 * connector lock before destroying the connector, to avoid AB->BA
306519c468b4SFrançois Tigeot 	 * ordering between this lock and the config mutex.
306619c468b4SFrançois Tigeot 	 */
306719c468b4SFrançois Tigeot 	for (;;) {
306819c468b4SFrançois Tigeot 		mutex_lock(&mgr->destroy_connector_lock);
306919c468b4SFrançois Tigeot 		port = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_dp_mst_port, next);
307019c468b4SFrançois Tigeot 		if (!port) {
307119c468b4SFrançois Tigeot 			mutex_unlock(&mgr->destroy_connector_lock);
307219c468b4SFrançois Tigeot 			break;
307319c468b4SFrançois Tigeot 		}
307419c468b4SFrançois Tigeot 		list_del(&port->next);
307519c468b4SFrançois Tigeot 		mutex_unlock(&mgr->destroy_connector_lock);
307619c468b4SFrançois Tigeot 
3077aee94f86SFrançois Tigeot 		kref_init(&port->kref);
3078aee94f86SFrançois Tigeot 		INIT_LIST_HEAD(&port->next);
3079aee94f86SFrançois Tigeot 
308019c468b4SFrançois Tigeot 		mgr->cbs->destroy_connector(mgr, port->connector);
308119c468b4SFrançois Tigeot 
308219c468b4SFrançois Tigeot 		drm_dp_port_teardown_pdt(port, port->pdt);
30831dedbd3bSFrançois Tigeot 		port->pdt = DP_PEER_DEVICE_NONE;
308419c468b4SFrançois Tigeot 
3085aee94f86SFrançois Tigeot 		if (!port->input && port->vcpi.vcpi > 0) {
3086aee94f86SFrançois Tigeot 			drm_dp_mst_reset_vcpi_slots(mgr, port);
3087aee94f86SFrançois Tigeot 			drm_dp_update_payload_part1(mgr);
308819c468b4SFrançois Tigeot 			drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
3089aee94f86SFrançois Tigeot 		}
3090aee94f86SFrançois Tigeot 
3091aee94f86SFrançois Tigeot 		kref_put(&port->kref, drm_dp_free_mst_port);
3092a05eeebfSFrançois Tigeot 		send_hotplug = true;
309319c468b4SFrançois Tigeot 	}
3094a05eeebfSFrançois Tigeot 	if (send_hotplug)
3095a05eeebfSFrançois Tigeot 		(*mgr->cbs->hotplug)(mgr);
309619c468b4SFrançois Tigeot }
309719c468b4SFrançois Tigeot 
3098*3f2dd94aSFrançois Tigeot static struct drm_private_state *
drm_dp_mst_duplicate_state(struct drm_private_obj * obj)3099*3f2dd94aSFrançois Tigeot drm_dp_mst_duplicate_state(struct drm_private_obj *obj)
3100*3f2dd94aSFrançois Tigeot {
3101*3f2dd94aSFrançois Tigeot 	struct drm_dp_mst_topology_state *state;
3102*3f2dd94aSFrançois Tigeot 
3103*3f2dd94aSFrançois Tigeot 	state = kmemdup(obj->state, sizeof(*state), GFP_KERNEL);
3104*3f2dd94aSFrançois Tigeot 	if (!state)
3105*3f2dd94aSFrançois Tigeot 		return NULL;
3106*3f2dd94aSFrançois Tigeot 
3107*3f2dd94aSFrançois Tigeot 	__drm_atomic_helper_private_obj_duplicate_state(obj, &state->base);
3108*3f2dd94aSFrançois Tigeot 
3109*3f2dd94aSFrançois Tigeot 	return &state->base;
3110*3f2dd94aSFrançois Tigeot }
3111*3f2dd94aSFrançois Tigeot 
drm_dp_mst_destroy_state(struct drm_private_obj * obj,struct drm_private_state * state)3112*3f2dd94aSFrançois Tigeot static void drm_dp_mst_destroy_state(struct drm_private_obj *obj,
3113*3f2dd94aSFrançois Tigeot 				     struct drm_private_state *state)
3114*3f2dd94aSFrançois Tigeot {
3115*3f2dd94aSFrançois Tigeot 	struct drm_dp_mst_topology_state *mst_state =
3116*3f2dd94aSFrançois Tigeot 		to_dp_mst_topology_state(state);
3117*3f2dd94aSFrançois Tigeot 
3118*3f2dd94aSFrançois Tigeot 	kfree(mst_state);
3119*3f2dd94aSFrançois Tigeot }
3120*3f2dd94aSFrançois Tigeot 
3121*3f2dd94aSFrançois Tigeot static const struct drm_private_state_funcs mst_state_funcs = {
3122*3f2dd94aSFrançois Tigeot 	.atomic_duplicate_state = drm_dp_mst_duplicate_state,
3123*3f2dd94aSFrançois Tigeot 	.atomic_destroy_state = drm_dp_mst_destroy_state,
3124*3f2dd94aSFrançois Tigeot };
3125*3f2dd94aSFrançois Tigeot 
3126*3f2dd94aSFrançois Tigeot /**
3127*3f2dd94aSFrançois Tigeot  * drm_atomic_get_mst_topology_state: get MST topology state
3128*3f2dd94aSFrançois Tigeot  *
3129*3f2dd94aSFrançois Tigeot  * @state: global atomic state
3130*3f2dd94aSFrançois Tigeot  * @mgr: MST topology manager, also the private object in this case
3131*3f2dd94aSFrançois Tigeot  *
3132*3f2dd94aSFrançois Tigeot  * This function wraps drm_atomic_get_priv_obj_state() passing in the MST atomic
3133*3f2dd94aSFrançois Tigeot  * state vtable so that the private object state returned is that of a MST
3134*3f2dd94aSFrançois Tigeot  * topology object. Also, drm_atomic_get_private_obj_state() expects the caller
3135*3f2dd94aSFrançois Tigeot  * to care of the locking, so warn if don't hold the connection_mutex.
3136*3f2dd94aSFrançois Tigeot  *
3137*3f2dd94aSFrançois Tigeot  * RETURNS:
3138*3f2dd94aSFrançois Tigeot  *
3139*3f2dd94aSFrançois Tigeot  * The MST topology state or error pointer.
3140*3f2dd94aSFrançois Tigeot  */
drm_atomic_get_mst_topology_state(struct drm_atomic_state * state,struct drm_dp_mst_topology_mgr * mgr)3141*3f2dd94aSFrançois Tigeot struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
3142*3f2dd94aSFrançois Tigeot 								    struct drm_dp_mst_topology_mgr *mgr)
3143*3f2dd94aSFrançois Tigeot {
3144*3f2dd94aSFrançois Tigeot 	struct drm_device *dev = mgr->dev;
3145*3f2dd94aSFrançois Tigeot 
3146*3f2dd94aSFrançois Tigeot 	WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex));
3147*3f2dd94aSFrançois Tigeot 	return to_dp_mst_topology_state(drm_atomic_get_private_obj_state(state, &mgr->base));
3148*3f2dd94aSFrançois Tigeot }
3149*3f2dd94aSFrançois Tigeot EXPORT_SYMBOL(drm_atomic_get_mst_topology_state);
3150*3f2dd94aSFrançois Tigeot 
315124edb884SFrançois Tigeot /**
315224edb884SFrançois Tigeot  * drm_dp_mst_topology_mgr_init - initialise a topology manager
315324edb884SFrançois Tigeot  * @mgr: manager struct to initialise
315424edb884SFrançois Tigeot  * @dev: device providing this structure - for i2c addition.
315524edb884SFrançois Tigeot  * @aux: DP helper aux channel to talk to this device
315624edb884SFrançois Tigeot  * @max_dpcd_transaction_bytes: hw specific DPCD transaction limit
315724edb884SFrançois Tigeot  * @max_payloads: maximum number of payloads this GPU can source
315824edb884SFrançois Tigeot  * @conn_base_id: the connector object ID the MST device is connected to.
315924edb884SFrançois Tigeot  *
316024edb884SFrançois Tigeot  * Return 0 for success, or negative error code on failure
316124edb884SFrançois Tigeot  */
drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr * mgr,struct drm_device * dev,struct drm_dp_aux * aux,int max_dpcd_transaction_bytes,int max_payloads,int conn_base_id)316224edb884SFrançois Tigeot int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
3163a85cb24fSFrançois Tigeot 				 struct drm_device *dev, struct drm_dp_aux *aux,
316424edb884SFrançois Tigeot 				 int max_dpcd_transaction_bytes,
316524edb884SFrançois Tigeot 				 int max_payloads, int conn_base_id)
316624edb884SFrançois Tigeot {
3167*3f2dd94aSFrançois Tigeot 	struct drm_dp_mst_topology_state *mst_state;
3168*3f2dd94aSFrançois Tigeot 
3169aee94f86SFrançois Tigeot 	lockinit(&mgr->lock, "drmml", 0, LK_CANRECURSE);
3170aee94f86SFrançois Tigeot 	lockinit(&mgr->qlock, "drmmql", 0, LK_CANRECURSE);
3171aee94f86SFrançois Tigeot 	lockinit(&mgr->payload_lock, "drmmpl", 0, LK_CANRECURSE);
3172aee94f86SFrançois Tigeot 	lockinit(&mgr->destroy_connector_lock, "drmmdcl", 0, LK_CANRECURSE);
317324edb884SFrançois Tigeot 	INIT_LIST_HEAD(&mgr->tx_msg_downq);
317419c468b4SFrançois Tigeot 	INIT_LIST_HEAD(&mgr->destroy_connector_list);
317524edb884SFrançois Tigeot 	INIT_WORK(&mgr->work, drm_dp_mst_link_probe_work);
317624edb884SFrançois Tigeot 	INIT_WORK(&mgr->tx_work, drm_dp_tx_work);
317719c468b4SFrançois Tigeot 	INIT_WORK(&mgr->destroy_connector_work, drm_dp_destroy_connector_work);
317824edb884SFrançois Tigeot 	init_waitqueue_head(&mgr->tx_waitq);
317924edb884SFrançois Tigeot 	mgr->dev = dev;
318024edb884SFrançois Tigeot 	mgr->aux = aux;
318124edb884SFrançois Tigeot 	mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
318224edb884SFrançois Tigeot 	mgr->max_payloads = max_payloads;
318324edb884SFrançois Tigeot 	mgr->conn_base_id = conn_base_id;
3184aee94f86SFrançois Tigeot 	if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 ||
3185aee94f86SFrançois Tigeot 	    max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8)
3186aee94f86SFrançois Tigeot 		return -EINVAL;
318724edb884SFrançois Tigeot 	mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
318824edb884SFrançois Tigeot 	if (!mgr->payloads)
318924edb884SFrançois Tigeot 		return -ENOMEM;
319024edb884SFrançois Tigeot 	mgr->proposed_vcpis = kcalloc(max_payloads, sizeof(struct drm_dp_vcpi *), GFP_KERNEL);
319124edb884SFrançois Tigeot 	if (!mgr->proposed_vcpis)
319224edb884SFrançois Tigeot 		return -ENOMEM;
319324edb884SFrançois Tigeot 	set_bit(0, &mgr->payload_mask);
3194aee94f86SFrançois Tigeot 	if (test_calc_pbn_mode() < 0)
3195aee94f86SFrançois Tigeot 		DRM_ERROR("MST PBN self-test failed\n");
3196aee94f86SFrançois Tigeot 
3197*3f2dd94aSFrançois Tigeot 	mst_state = kzalloc(sizeof(*mst_state), GFP_KERNEL);
3198*3f2dd94aSFrançois Tigeot 	if (mst_state == NULL)
3199*3f2dd94aSFrançois Tigeot 		return -ENOMEM;
3200*3f2dd94aSFrançois Tigeot 
3201*3f2dd94aSFrançois Tigeot 	mst_state->mgr = mgr;
3202*3f2dd94aSFrançois Tigeot 
3203*3f2dd94aSFrançois Tigeot 	/* max. time slots - one slot for MTP header */
3204*3f2dd94aSFrançois Tigeot 	mst_state->avail_slots = 63;
3205*3f2dd94aSFrançois Tigeot 
3206*3f2dd94aSFrançois Tigeot 	drm_atomic_private_obj_init(&mgr->base,
3207*3f2dd94aSFrançois Tigeot 				    &mst_state->base,
3208*3f2dd94aSFrançois Tigeot 				    &mst_state_funcs);
3209*3f2dd94aSFrançois Tigeot 
321024edb884SFrançois Tigeot 	return 0;
321124edb884SFrançois Tigeot }
321224edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
321324edb884SFrançois Tigeot 
321424edb884SFrançois Tigeot /**
321524edb884SFrançois Tigeot  * drm_dp_mst_topology_mgr_destroy() - destroy topology manager.
321624edb884SFrançois Tigeot  * @mgr: manager to destroy
321724edb884SFrançois Tigeot  */
drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr * mgr)321824edb884SFrançois Tigeot void drm_dp_mst_topology_mgr_destroy(struct drm_dp_mst_topology_mgr *mgr)
321924edb884SFrançois Tigeot {
3220a05eeebfSFrançois Tigeot 	flush_work(&mgr->work);
322119c468b4SFrançois Tigeot 	flush_work(&mgr->destroy_connector_work);
322224edb884SFrançois Tigeot 	mutex_lock(&mgr->payload_lock);
322324edb884SFrançois Tigeot 	kfree(mgr->payloads);
322424edb884SFrançois Tigeot 	mgr->payloads = NULL;
322524edb884SFrançois Tigeot 	kfree(mgr->proposed_vcpis);
322624edb884SFrançois Tigeot 	mgr->proposed_vcpis = NULL;
322724edb884SFrançois Tigeot 	mutex_unlock(&mgr->payload_lock);
322824edb884SFrançois Tigeot 	mgr->dev = NULL;
322924edb884SFrançois Tigeot 	mgr->aux = NULL;
3230*3f2dd94aSFrançois Tigeot 	drm_atomic_private_obj_fini(&mgr->base);
3231*3f2dd94aSFrançois Tigeot 	mgr->funcs = NULL;
323224edb884SFrançois Tigeot }
323324edb884SFrançois Tigeot EXPORT_SYMBOL(drm_dp_mst_topology_mgr_destroy);
323424edb884SFrançois Tigeot 
323524edb884SFrançois Tigeot /* I2C device */
drm_dp_mst_i2c_xfer(struct i2c_adapter * adapter,struct i2c_msg * msgs,int num)323624edb884SFrançois Tigeot static int drm_dp_mst_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
323724edb884SFrançois Tigeot 			       int num)
323824edb884SFrançois Tigeot {
323924edb884SFrançois Tigeot 	struct drm_dp_aux *aux = adapter->algo_data;
324024edb884SFrançois Tigeot 	struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port, aux);
324124edb884SFrançois Tigeot 	struct drm_dp_mst_branch *mstb;
324224edb884SFrançois Tigeot 	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
324324edb884SFrançois Tigeot 	unsigned int i;
324424edb884SFrançois Tigeot 	bool reading = false;
324524edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_req_body msg;
324624edb884SFrançois Tigeot 	struct drm_dp_sideband_msg_tx *txmsg = NULL;
324724edb884SFrançois Tigeot 	int ret;
324824edb884SFrançois Tigeot 
324924edb884SFrançois Tigeot 	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
325024edb884SFrançois Tigeot 	if (!mstb)
325124edb884SFrançois Tigeot 		return -EREMOTEIO;
325224edb884SFrançois Tigeot 
325324edb884SFrançois Tigeot 	/* construct i2c msg */
325424edb884SFrançois Tigeot 	/* see if last msg is a read */
325524edb884SFrançois Tigeot 	if (msgs[num - 1].flags & I2C_M_RD)
325624edb884SFrançois Tigeot 		reading = true;
325724edb884SFrançois Tigeot 
3258a05eeebfSFrançois Tigeot 	if (!reading || (num - 1 > DP_REMOTE_I2C_READ_MAX_TRANSACTIONS)) {
325924edb884SFrançois Tigeot 		DRM_DEBUG_KMS("Unsupported I2C transaction for MST device\n");
326024edb884SFrançois Tigeot 		ret = -EIO;
326124edb884SFrançois Tigeot 		goto out;
326224edb884SFrançois Tigeot 	}
326324edb884SFrançois Tigeot 
3264a05eeebfSFrançois Tigeot 	memset(&msg, 0, sizeof(msg));
326524edb884SFrançois Tigeot 	msg.req_type = DP_REMOTE_I2C_READ;
326624edb884SFrançois Tigeot 	msg.u.i2c_read.num_transactions = num - 1;
326724edb884SFrançois Tigeot 	msg.u.i2c_read.port_number = port->port_num;
326824edb884SFrançois Tigeot 	for (i = 0; i < num - 1; i++) {
326924edb884SFrançois Tigeot 		msg.u.i2c_read.transactions[i].i2c_dev_id = msgs[i].addr;
327024edb884SFrançois Tigeot 		msg.u.i2c_read.transactions[i].num_bytes = msgs[i].len;
327124edb884SFrançois Tigeot 		msg.u.i2c_read.transactions[i].bytes = msgs[i].buf;
327224edb884SFrançois Tigeot 	}
327324edb884SFrançois Tigeot 	msg.u.i2c_read.read_i2c_device_id = msgs[num - 1].addr;
327424edb884SFrançois Tigeot 	msg.u.i2c_read.num_bytes_read = msgs[num - 1].len;
327524edb884SFrançois Tigeot 
327624edb884SFrançois Tigeot 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
327724edb884SFrançois Tigeot 	if (!txmsg) {
327824edb884SFrançois Tigeot 		ret = -ENOMEM;
327924edb884SFrançois Tigeot 		goto out;
328024edb884SFrançois Tigeot 	}
328124edb884SFrançois Tigeot 
328224edb884SFrançois Tigeot 	txmsg->dst = mstb;
328324edb884SFrançois Tigeot 	drm_dp_encode_sideband_req(&msg, txmsg);
328424edb884SFrançois Tigeot 
328524edb884SFrançois Tigeot 	drm_dp_queue_down_tx(mgr, txmsg);
328624edb884SFrançois Tigeot 
328724edb884SFrançois Tigeot 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
328824edb884SFrançois Tigeot 	if (ret > 0) {
328924edb884SFrançois Tigeot 
329024edb884SFrançois Tigeot 		if (txmsg->reply.reply_type == 1) { /* got a NAK back */
329124edb884SFrançois Tigeot 			ret = -EREMOTEIO;
329224edb884SFrançois Tigeot 			goto out;
329324edb884SFrançois Tigeot 		}
329424edb884SFrançois Tigeot 		if (txmsg->reply.u.remote_i2c_read_ack.num_bytes != msgs[num - 1].len) {
329524edb884SFrançois Tigeot 			ret = -EIO;
329624edb884SFrançois Tigeot 			goto out;
329724edb884SFrançois Tigeot 		}
329824edb884SFrançois Tigeot 		memcpy(msgs[num - 1].buf, txmsg->reply.u.remote_i2c_read_ack.bytes, msgs[num - 1].len);
329924edb884SFrançois Tigeot 		ret = num;
330024edb884SFrançois Tigeot 	}
330124edb884SFrançois Tigeot out:
330224edb884SFrançois Tigeot 	kfree(txmsg);
330324edb884SFrançois Tigeot 	drm_dp_put_mst_branch_device(mstb);
330424edb884SFrançois Tigeot 	return ret;
330524edb884SFrançois Tigeot }
330624edb884SFrançois Tigeot 
drm_dp_mst_i2c_functionality(struct i2c_adapter * adapter)330724edb884SFrançois Tigeot static u32 drm_dp_mst_i2c_functionality(struct i2c_adapter *adapter)
330824edb884SFrançois Tigeot {
330924edb884SFrançois Tigeot 	return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
331024edb884SFrançois Tigeot 	       I2C_FUNC_SMBUS_READ_BLOCK_DATA |
331124edb884SFrançois Tigeot 	       I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
331224edb884SFrançois Tigeot 	       I2C_FUNC_10BIT_ADDR;
331324edb884SFrançois Tigeot }
331424edb884SFrançois Tigeot 
331524edb884SFrançois Tigeot static const struct i2c_algorithm drm_dp_mst_i2c_algo = {
331624edb884SFrançois Tigeot 	.functionality = drm_dp_mst_i2c_functionality,
331724edb884SFrançois Tigeot 	.master_xfer = drm_dp_mst_i2c_xfer,
331824edb884SFrançois Tigeot };
331924edb884SFrançois Tigeot 
332024edb884SFrançois Tigeot /**
332124edb884SFrançois Tigeot  * drm_dp_mst_register_i2c_bus() - register an I2C adapter for I2C-over-AUX
332224edb884SFrançois Tigeot  * @aux: DisplayPort AUX channel
332324edb884SFrançois Tigeot  *
332424edb884SFrançois Tigeot  * Returns 0 on success or a negative error code on failure.
332524edb884SFrançois Tigeot  */
drm_dp_mst_register_i2c_bus(struct drm_dp_aux * aux)332624edb884SFrançois Tigeot static int drm_dp_mst_register_i2c_bus(struct drm_dp_aux *aux)
332724edb884SFrançois Tigeot {
332824edb884SFrançois Tigeot 	aux->ddc.algo = &drm_dp_mst_i2c_algo;
332924edb884SFrançois Tigeot 	aux->ddc.algo_data = aux;
333024edb884SFrançois Tigeot 	aux->ddc.retries = 3;
333124edb884SFrançois Tigeot 
3332aee94f86SFrançois Tigeot #if 0
333324edb884SFrançois Tigeot 	aux->ddc.class = I2C_CLASS_DDC;
333424edb884SFrançois Tigeot 	aux->ddc.owner = THIS_MODULE;
3335aee94f86SFrançois Tigeot #endif
333624edb884SFrançois Tigeot 	aux->ddc.dev.parent = aux->dev;
3337aee94f86SFrançois Tigeot #if 0
333824edb884SFrançois Tigeot 	aux->ddc.dev.of_node = aux->dev->of_node;
3339aee94f86SFrançois Tigeot #endif
334024edb884SFrançois Tigeot 
334124edb884SFrançois Tigeot 	strlcpy(aux->ddc.name, aux->name ? aux->name : dev_name(aux->dev),
334224edb884SFrançois Tigeot 		sizeof(aux->ddc.name));
334324edb884SFrançois Tigeot 
334424edb884SFrançois Tigeot 	return i2c_add_adapter(&aux->ddc);
334524edb884SFrançois Tigeot }
334624edb884SFrançois Tigeot 
334724edb884SFrançois Tigeot /**
334824edb884SFrançois Tigeot  * drm_dp_mst_unregister_i2c_bus() - unregister an I2C-over-AUX adapter
334924edb884SFrançois Tigeot  * @aux: DisplayPort AUX channel
335024edb884SFrançois Tigeot  */
drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux * aux)335124edb884SFrançois Tigeot static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_aux *aux)
335224edb884SFrançois Tigeot {
335324edb884SFrançois Tigeot 	i2c_del_adapter(&aux->ddc);
335424edb884SFrançois Tigeot }
3355