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