Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Linux/drivers/gpu/drm/display/   (Open Source Betriebssystem Version 6.17.9©)  Datei vom 24.10.2025 mit Größe 175 kB image not shown  

Quelle  drm_dp_mst_topology.c   Sprache: C

 
/*
 * Copyright © 2014 Red Hat
 *
 * Permission to use, copy, modify, distribute, and sell this software and its
 * documentation for any purpose is hereby granted without fee, provided that
 * the above copyright notice appear in all copies and that both that copyright
 * notice and this permission notice appear in supporting documentation, and
 * that the name of the copyright holders not be used in advertising or
 * publicity pertaining to distribution of the software without specific,
 * written prior permission.  The copyright holders make no representations
 * about the suitability of this software for any purpose.  It is provided "as
 * is" without express or implied warranty.
 *
 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
 * OF THIS SOFTWARE.
 */


#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/export.h>
#include <linux/i2c.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/random.h>
#include <linux/sched.h>
#include <linux/seq_file.h>

#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)
#include <linux/stacktrace.h>
#include <linux/sort.h>
#include <linux/timekeeping.h>
#include <linux/math64.h>
#endif

#include <drm/display/drm_dp_mst_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_atomic_helper.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_fixed.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>

#include "drm_dp_helper_internal.h"
#include "drm_dp_mst_topology_internal.h"

/**
 * DOC: dp mst helper
 *
 * These functions contain parts of the DisplayPort 1.2a MultiStream Transport
 * protocol. The helpers contain a topology manager and bandwidth manager.
 * The helpers encapsulate the sending and received of sideband msgs.
 */

struct drm_dp_pending_up_req {
 struct drm_dp_sideband_msg_hdr hdr;
 struct drm_dp_sideband_msg_req_body msg;
 struct list_head next;
};

static bool dump_dp_payload_table(struct drm_dp_mst_topology_mgr *mgr,
      char *buf);

static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port);

static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
     struct drm_dp_mst_port *port,
     int offset, int size, u8 *bytes);
static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
      struct drm_dp_mst_port *port,
      int offset, int size, u8 *bytes);

static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
        struct drm_dp_mst_branch *mstb);

static void
drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
       struct drm_dp_mst_branch *mstb);

static int 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);
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
     guid_t *guid);

static int drm_dp_mst_register_i2c_bus(struct drm_dp_mst_port *port);
static void drm_dp_mst_unregister_i2c_bus(struct drm_dp_mst_port *port);
static void drm_dp_mst_kick_tx(struct drm_dp_mst_topology_mgr *mgr);

static bool drm_dp_mst_port_downstream_of_branch(struct drm_dp_mst_port *port,
       struct drm_dp_mst_branch *branch);

#define DBG_PREFIX "[dp_mst]"

#define DP_STR(x) [DP_ ## x] = #x

static const char *drm_dp_mst_req_type_str(u8 req_type)
{
 static const char * const req_type_str[] = {
  DP_STR(GET_MSG_TRANSACTION_VERSION),
  DP_STR(LINK_ADDRESS),
  DP_STR(CONNECTION_STATUS_NOTIFY),
  DP_STR(ENUM_PATH_RESOURCES),
  DP_STR(ALLOCATE_PAYLOAD),
  DP_STR(QUERY_PAYLOAD),
  DP_STR(RESOURCE_STATUS_NOTIFY),
  DP_STR(CLEAR_PAYLOAD_ID_TABLE),
  DP_STR(REMOTE_DPCD_READ),
  DP_STR(REMOTE_DPCD_WRITE),
  DP_STR(REMOTE_I2C_READ),
  DP_STR(REMOTE_I2C_WRITE),
  DP_STR(POWER_UP_PHY),
  DP_STR(POWER_DOWN_PHY),
  DP_STR(SINK_EVENT_NOTIFY),
  DP_STR(QUERY_STREAM_ENC_STATUS),
 };

 if (req_type >= ARRAY_SIZE(req_type_str) ||
     !req_type_str[req_type])
  return "unknown";

 return req_type_str[req_type];
}

#undef DP_STR
#define DP_STR(x) [DP_NAK_ ## x] = #x

static const char *drm_dp_mst_nak_reason_str(u8 nak_reason)
{
 static const char * const nak_reason_str[] = {
  DP_STR(WRITE_FAILURE),
  DP_STR(INVALID_READ),
  DP_STR(CRC_FAILURE),
  DP_STR(BAD_PARAM),
  DP_STR(DEFER),
  DP_STR(LINK_FAILURE),
  DP_STR(NO_RESOURCES),
  DP_STR(DPCD_FAIL),
  DP_STR(I2C_NAK),
  DP_STR(ALLOCATE_FAIL),
 };

 if (nak_reason >= ARRAY_SIZE(nak_reason_str) ||
     !nak_reason_str[nak_reason])
  return "unknown";

 return nak_reason_str[nak_reason];
}

#undef DP_STR
#define DP_STR(x) [DRM_DP_SIDEBAND_TX_ ## x] = #x

static const char *drm_dp_mst_sideband_tx_state_str(int state)
{
 static const char * const sideband_reason_str[] = {
  DP_STR(QUEUED),
  DP_STR(START_SEND),
  DP_STR(SENT),
  DP_STR(RX),
  DP_STR(TIMEOUT),
 };

 if (state >= ARRAY_SIZE(sideband_reason_str) ||
     !sideband_reason_str[state])
  return "unknown";

 return sideband_reason_str[state];
}

static inline u8
drm_dp_mst_get_ufp_num_at_lct_from_rad(u8 lct, const u8 *rad)
{
 int idx = (lct / 2) - 1;
 int shift = (lct % 2) ? 0 : 4;
 u8 ufp_num;

 /* mst_primary, it's rad is unset*/
 if (lct == 1)
  return 0;

 ufp_num = (rad[idx] >> shift) & 0xf;

 return ufp_num;
}

static int
drm_dp_mst_rad_to_str(const u8 rad[8], u8 lct, char *out, size_t len)
{
 int i;
 u8 unpacked_rad[16] = {};

 for (i = 0; i < lct; i++)
  unpacked_rad[i] = drm_dp_mst_get_ufp_num_at_lct_from_rad(i + 1, rad);

 /* TODO: Eventually add something to printk so we can format the rad
 * like this: 1.2.3
 */

 return snprintf(out, len, "%*phC", lct, unpacked_rad);
}

/* sideband msg handling */
static u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles)
{
 u8 bitmask = 0x80;
 u8 bitshift = 7;
 u8 array_index = 0;
 int number_of_bits = num_nibbles * 4;
 u8 remainder = 0;

 while (number_of_bits != 0) {
  number_of_bits--;
  remainder <<= 1;
  remainder |= (data[array_index] & bitmask) >> bitshift;
  bitmask >>= 1;
  bitshift--;
  if (bitmask == 0) {
   bitmask = 0x80;
   bitshift = 7;
   array_index++;
  }
  if ((remainder & 0x10) == 0x10)
   remainder ^= 0x13;
 }

 number_of_bits = 4;
 while (number_of_bits != 0) {
  number_of_bits--;
  remainder <<= 1;
  if ((remainder & 0x10) != 0)
   remainder ^= 0x13;
 }

 return remainder;
}

static u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes)
{
 u8 bitmask = 0x80;
 u8 bitshift = 7;
 u8 array_index = 0;
 int number_of_bits = number_of_bytes * 8;
 u16 remainder = 0;

 while (number_of_bits != 0) {
  number_of_bits--;
  remainder <<= 1;
  remainder |= (data[array_index] & bitmask) >> bitshift;
  bitmask >>= 1;
  bitshift--;
  if (bitmask == 0) {
   bitmask = 0x80;
   bitshift = 7;
   array_index++;
  }
  if ((remainder & 0x100) == 0x100)
   remainder ^= 0xd5;
 }

 number_of_bits = 8;
 while (number_of_bits != 0) {
  number_of_bits--;
  remainder <<= 1;
  if ((remainder & 0x100) != 0)
   remainder ^= 0xd5;
 }

 return remainder & 0xff;
}
static inline u8 drm_dp_calc_sb_hdr_size(struct drm_dp_sideband_msg_hdr *hdr)
{
 u8 size = 3;

 size += (hdr->lct / 2);
 return size;
}

static void drm_dp_encode_sideband_msg_hdr(struct drm_dp_sideband_msg_hdr *hdr,
        u8 *buf, int *len)
{
 int idx = 0;
 int i;
 u8 crc4;

 buf[idx++] = ((hdr->lct & 0xf) << 4) | (hdr->lcr & 0xf);
 for (i = 0; i < (hdr->lct / 2); i++)
  buf[idx++] = hdr->rad[i];
 buf[idx++] = (hdr->broadcast << 7) | (hdr->path_msg << 6) |
  (hdr->msg_len & 0x3f);
 buf[idx++] = (hdr->somt << 7) | (hdr->eomt << 6) | (hdr->seqno << 4);

 crc4 = drm_dp_msg_header_crc4(buf, (idx * 2) - 1);
 buf[idx - 1] |= (crc4 & 0xf);

 *len = idx;
}

static bool drm_dp_decode_sideband_msg_hdr(const struct drm_dp_mst_topology_mgr *mgr,
        struct drm_dp_sideband_msg_hdr *hdr,
        u8 *buf, int buflen, u8 *hdrlen)
{
 u8 crc4;
 u8 len;
 int i;
 u8 idx;

 if (buf[0] == 0)
  return false;
 len = 3;
 len += ((buf[0] & 0xf0) >> 4) / 2;
 if (len > buflen)
  return false;
 crc4 = drm_dp_msg_header_crc4(buf, (len * 2) - 1);

 if ((crc4 & 0xf) != (buf[len - 1] & 0xf)) {
  drm_dbg_kms(mgr->dev, "crc4 mismatch 0x%x 0x%x\n", crc4, buf[len - 1]);
  return false;
 }

 hdr->lct = (buf[0] & 0xf0) >> 4;
 hdr->lcr = (buf[0] & 0xf);
 idx = 1;
 for (i = 0; i < (hdr->lct / 2); i++)
  hdr->rad[i] = buf[idx++];
 hdr->broadcast = (buf[idx] >> 7) & 0x1;
 hdr->path_msg = (buf[idx] >> 6) & 0x1;
 hdr->msg_len = buf[idx] & 0x3f;
 if (hdr->msg_len < 1)  /* min space for body CRC */
  return false;

 idx++;
 hdr->somt = (buf[idx] >> 7) & 0x1;
 hdr->eomt = (buf[idx] >> 6) & 0x1;
 hdr->seqno = (buf[idx] >> 4) & 0x1;
 idx++;
 *hdrlen = idx;
 return true;
}

void
drm_dp_encode_sideband_req(const struct drm_dp_sideband_msg_req_body *req,
      struct drm_dp_sideband_msg_tx *raw)
{
 int idx = 0;
 int i;
 u8 *buf = raw->msg;

 buf[idx++] = req->req_type & 0x7f;

 switch (req->req_type) {
 case DP_ENUM_PATH_RESOURCES:
 case DP_POWER_DOWN_PHY:
 case DP_POWER_UP_PHY:
  buf[idx] = (req->u.port_num.port_number & 0xf) << 4;
  idx++;
  break;
 case DP_ALLOCATE_PAYLOAD:
  buf[idx] = (req->u.allocate_payload.port_number & 0xf) << 4 |
   (req->u.allocate_payload.number_sdp_streams & 0xf);
  idx++;
  buf[idx] = (req->u.allocate_payload.vcpi & 0x7f);
  idx++;
  buf[idx] = (req->u.allocate_payload.pbn >> 8);
  idx++;
  buf[idx] = (req->u.allocate_payload.pbn & 0xff);
  idx++;
  for (i = 0; i < req->u.allocate_payload.number_sdp_streams / 2; i++) {
   buf[idx] = ((req->u.allocate_payload.sdp_stream_sink[i * 2] & 0xf) << 4) |
    (req->u.allocate_payload.sdp_stream_sink[i * 2 + 1] & 0xf);
   idx++;
  }
  if (req->u.allocate_payload.number_sdp_streams & 1) {
   i = req->u.allocate_payload.number_sdp_streams - 1;
   buf[idx] = (req->u.allocate_payload.sdp_stream_sink[i] & 0xf) << 4;
   idx++;
  }
  break;
 case DP_QUERY_PAYLOAD:
  buf[idx] = (req->u.query_payload.port_number & 0xf) << 4;
  idx++;
  buf[idx] = (req->u.query_payload.vcpi & 0x7f);
  idx++;
  break;
 case DP_REMOTE_DPCD_READ:
  buf[idx] = (req->u.dpcd_read.port_number & 0xf) << 4;
  buf[idx] |= ((req->u.dpcd_read.dpcd_address & 0xf0000) >> 16) & 0xf;
  idx++;
  buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff00) >> 8;
  idx++;
  buf[idx] = (req->u.dpcd_read.dpcd_address & 0xff);
  idx++;
  buf[idx] = (req->u.dpcd_read.num_bytes);
  idx++;
  break;

 case DP_REMOTE_DPCD_WRITE:
  buf[idx] = (req->u.dpcd_write.port_number & 0xf) << 4;
  buf[idx] |= ((req->u.dpcd_write.dpcd_address & 0xf0000) >> 16) & 0xf;
  idx++;
  buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff00) >> 8;
  idx++;
  buf[idx] = (req->u.dpcd_write.dpcd_address & 0xff);
  idx++;
  buf[idx] = (req->u.dpcd_write.num_bytes);
  idx++;
  memcpy(&buf[idx], req->u.dpcd_write.bytes, req->u.dpcd_write.num_bytes);
  idx += req->u.dpcd_write.num_bytes;
  break;
 case DP_REMOTE_I2C_READ:
  buf[idx] = (req->u.i2c_read.port_number & 0xf) << 4;
  buf[idx] |= (req->u.i2c_read.num_transactions & 0x3);
  idx++;
  for (i = 0; i < (req->u.i2c_read.num_transactions & 0x3); i++) {
   buf[idx] = req->u.i2c_read.transactions[i].i2c_dev_id & 0x7f;
   idx++;
   buf[idx] = req->u.i2c_read.transactions[i].num_bytes;
   idx++;
   memcpy(&buf[idx], req->u.i2c_read.transactions[i].bytes, req->u.i2c_read.transactions[i].num_bytes);
   idx += req->u.i2c_read.transactions[i].num_bytes;

   buf[idx] = (req->u.i2c_read.transactions[i].no_stop_bit & 0x1) << 4;
   buf[idx] |= (req->u.i2c_read.transactions[i].i2c_transaction_delay & 0xf);
   idx++;
  }
  buf[idx] = (req->u.i2c_read.read_i2c_device_id) & 0x7f;
  idx++;
  buf[idx] = (req->u.i2c_read.num_bytes_read);
  idx++;
  break;

 case DP_REMOTE_I2C_WRITE:
  buf[idx] = (req->u.i2c_write.port_number & 0xf) << 4;
  idx++;
  buf[idx] = (req->u.i2c_write.write_i2c_device_id) & 0x7f;
  idx++;
  buf[idx] = (req->u.i2c_write.num_bytes);
  idx++;
  memcpy(&buf[idx], req->u.i2c_write.bytes, req->u.i2c_write.num_bytes);
  idx += req->u.i2c_write.num_bytes;
  break;
 case DP_QUERY_STREAM_ENC_STATUS: {
  const struct drm_dp_query_stream_enc_status *msg;

  msg = &req->u.enc_status;
  buf[idx] = msg->stream_id;
  idx++;
  memcpy(&buf[idx], msg->client_id, sizeof(msg->client_id));
  idx += sizeof(msg->client_id);
  buf[idx] = 0;
  buf[idx] |= FIELD_PREP(GENMASK(1, 0), msg->stream_event);
  buf[idx] |= msg->valid_stream_event ? BIT(2) : 0;
  buf[idx] |= FIELD_PREP(GENMASK(4, 3), msg->stream_behavior);
  buf[idx] |= msg->valid_stream_behavior ? BIT(5) : 0;
  idx++;
  }
  break;
 }
 raw->cur_len = idx;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_encode_sideband_req);

/* Decode a sideband request we've encoded, mainly used for debugging */
int
drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
      struct drm_dp_sideband_msg_req_body *req)
{
 const u8 *buf = raw->msg;
 int i, idx = 0;

 req->req_type = buf[idx++] & 0x7f;
 switch (req->req_type) {
 case DP_ENUM_PATH_RESOURCES:
 case DP_POWER_DOWN_PHY:
 case DP_POWER_UP_PHY:
  req->u.port_num.port_number = (buf[idx] >> 4) & 0xf;
  break;
 case DP_ALLOCATE_PAYLOAD:
  {
   struct drm_dp_allocate_payload *a =
    &req->u.allocate_payload;

   a->number_sdp_streams = buf[idx] & 0xf;
   a->port_number = (buf[idx] >> 4) & 0xf;

   WARN_ON(buf[++idx] & 0x80);
   a->vcpi = buf[idx] & 0x7f;

   a->pbn = buf[++idx] << 8;
   a->pbn |= buf[++idx];

   idx++;
   for (i = 0; i < a->number_sdp_streams; i++) {
    a->sdp_stream_sink[i] =
     (buf[idx + (i / 2)] >> ((i % 2) ? 0 : 4)) & 0xf;
   }
  }
  break;
 case DP_QUERY_PAYLOAD:
  req->u.query_payload.port_number = (buf[idx] >> 4) & 0xf;
  WARN_ON(buf[++idx] & 0x80);
  req->u.query_payload.vcpi = buf[idx] & 0x7f;
  break;
 case DP_REMOTE_DPCD_READ:
  {
   struct drm_dp_remote_dpcd_read *r = &req->u.dpcd_read;

   r->port_number = (buf[idx] >> 4) & 0xf;

   r->dpcd_address = (buf[idx] << 16) & 0xf0000;
   r->dpcd_address |= (buf[++idx] << 8) & 0xff00;
   r->dpcd_address |= buf[++idx] & 0xff;

   r->num_bytes = buf[++idx];
  }
  break;
 case DP_REMOTE_DPCD_WRITE:
  {
   struct drm_dp_remote_dpcd_write *w =
    &req->u.dpcd_write;

   w->port_number = (buf[idx] >> 4) & 0xf;

   w->dpcd_address = (buf[idx] << 16) & 0xf0000;
   w->dpcd_address |= (buf[++idx] << 8) & 0xff00;
   w->dpcd_address |= buf[++idx] & 0xff;

   w->num_bytes = buf[++idx];

   w->bytes = kmemdup(&buf[++idx], w->num_bytes,
        GFP_KERNEL);
   if (!w->bytes)
    return -ENOMEM;
  }
  break;
 case DP_REMOTE_I2C_READ:
  {
   struct drm_dp_remote_i2c_read *r = &req->u.i2c_read;
   struct drm_dp_remote_i2c_read_tx *tx;
   bool failed = false;

   r->num_transactions = buf[idx] & 0x3;
   r->port_number = (buf[idx] >> 4) & 0xf;
   for (i = 0; i < r->num_transactions; i++) {
    tx = &r->transactions[i];

    tx->i2c_dev_id = buf[++idx] & 0x7f;
    tx->num_bytes = buf[++idx];
    tx->bytes = kmemdup(&buf[++idx],
          tx->num_bytes,
          GFP_KERNEL);
    if (!tx->bytes) {
     failed = true;
     break;
    }
    idx += tx->num_bytes;
    tx->no_stop_bit = (buf[idx] >> 5) & 0x1;
    tx->i2c_transaction_delay = buf[idx] & 0xf;
   }

   if (failed) {
    for (i = 0; i < r->num_transactions; i++) {
     tx = &r->transactions[i];
     kfree(tx->bytes);
    }
    return -ENOMEM;
   }

   r->read_i2c_device_id = buf[++idx] & 0x7f;
   r->num_bytes_read = buf[++idx];
  }
  break;
 case DP_REMOTE_I2C_WRITE:
  {
   struct drm_dp_remote_i2c_write *w = &req->u.i2c_write;

   w->port_number = (buf[idx] >> 4) & 0xf;
   w->write_i2c_device_id = buf[++idx] & 0x7f;
   w->num_bytes = buf[++idx];
   w->bytes = kmemdup(&buf[++idx], w->num_bytes,
        GFP_KERNEL);
   if (!w->bytes)
    return -ENOMEM;
  }
  break;
 case DP_QUERY_STREAM_ENC_STATUS:
  req->u.enc_status.stream_id = buf[idx++];
  for (i = 0; i < sizeof(req->u.enc_status.client_id); i++)
   req->u.enc_status.client_id[i] = buf[idx++];

  req->u.enc_status.stream_event = FIELD_GET(GENMASK(1, 0),
          buf[idx]);
  req->u.enc_status.valid_stream_event = FIELD_GET(BIT(2),
         buf[idx]);
  req->u.enc_status.stream_behavior = FIELD_GET(GENMASK(4, 3),
             buf[idx]);
  req->u.enc_status.valid_stream_behavior = FIELD_GET(BIT(5),
            buf[idx]);
  break;
 }

 return 0;
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_decode_sideband_req);

void
drm_dp_dump_sideband_msg_req_body(const struct drm_dp_sideband_msg_req_body *req,
      int indent, struct drm_printer *printer)
{
 int i;

#define P(f, ...) drm_printf_indent(printer, indent, f, ##__VA_ARGS__)
 if (req->req_type == DP_LINK_ADDRESS) {
  /* No contents to print */
  P("type=%s\n", drm_dp_mst_req_type_str(req->req_type));
  return;
 }

 P("type=%s contents:\n", drm_dp_mst_req_type_str(req->req_type));
 indent++;

 switch (req->req_type) {
 case DP_ENUM_PATH_RESOURCES:
 case DP_POWER_DOWN_PHY:
 case DP_POWER_UP_PHY:
  P("port=%d\n", req->u.port_num.port_number);
  break;
 case DP_ALLOCATE_PAYLOAD:
  P("port=%d vcpi=%d pbn=%d sdp_streams=%d %*ph\n",
    req->u.allocate_payload.port_number,
    req->u.allocate_payload.vcpi, req->u.allocate_payload.pbn,
    req->u.allocate_payload.number_sdp_streams,
    req->u.allocate_payload.number_sdp_streams,
    req->u.allocate_payload.sdp_stream_sink);
  break;
 case DP_QUERY_PAYLOAD:
  P("port=%d vcpi=%d\n",
    req->u.query_payload.port_number,
    req->u.query_payload.vcpi);
  break;
 case DP_REMOTE_DPCD_READ:
  P("port=%d dpcd_addr=%05x len=%d\n",
    req->u.dpcd_read.port_number, req->u.dpcd_read.dpcd_address,
    req->u.dpcd_read.num_bytes);
  break;
 case DP_REMOTE_DPCD_WRITE:
  P("port=%d addr=%05x len=%d: %*ph\n",
    req->u.dpcd_write.port_number,
    req->u.dpcd_write.dpcd_address,
    req->u.dpcd_write.num_bytes, req->u.dpcd_write.num_bytes,
    req->u.dpcd_write.bytes);
  break;
 case DP_REMOTE_I2C_READ:
  P("port=%d num_tx=%d id=%d size=%d:\n",
    req->u.i2c_read.port_number,
    req->u.i2c_read.num_transactions,
    req->u.i2c_read.read_i2c_device_id,
    req->u.i2c_read.num_bytes_read);

  indent++;
  for (i = 0; i < req->u.i2c_read.num_transactions; i++) {
   const struct drm_dp_remote_i2c_read_tx *rtx =
    &req->u.i2c_read.transactions[i];

   P("%d: id=%03d size=%03d no_stop_bit=%d tx_delay=%03d: %*ph\n",
     i, rtx->i2c_dev_id, rtx->num_bytes,
     rtx->no_stop_bit, rtx->i2c_transaction_delay,
     rtx->num_bytes, rtx->bytes);
  }
  break;
 case DP_REMOTE_I2C_WRITE:
  P("port=%d id=%d size=%d: %*ph\n",
    req->u.i2c_write.port_number,
    req->u.i2c_write.write_i2c_device_id,
    req->u.i2c_write.num_bytes, req->u.i2c_write.num_bytes,
    req->u.i2c_write.bytes);
  break;
 case DP_QUERY_STREAM_ENC_STATUS:
  P("stream_id=%u client_id=%*ph stream_event=%x "
    "valid_event=%d stream_behavior=%x valid_behavior=%d",
    req->u.enc_status.stream_id,
    (int)ARRAY_SIZE(req->u.enc_status.client_id),
    req->u.enc_status.client_id, req->u.enc_status.stream_event,
    req->u.enc_status.valid_stream_event,
    req->u.enc_status.stream_behavior,
    req->u.enc_status.valid_stream_behavior);
  break;
 default:
  P("???\n");
  break;
 }
#undef P
}
EXPORT_SYMBOL_FOR_TESTS_ONLY(drm_dp_dump_sideband_msg_req_body);

static inline void
drm_dp_mst_dump_sideband_msg_tx(struct drm_printer *p,
    const struct drm_dp_sideband_msg_tx *txmsg)
{
 struct drm_dp_sideband_msg_req_body req;
 char buf[64];
 int ret;
 int i;

 drm_dp_mst_rad_to_str(txmsg->dst->rad, txmsg->dst->lct, buf,
         sizeof(buf));
 drm_printf(p, "txmsg cur_offset=%x cur_len=%x seqno=%x state=%s path_msg=%d dst=%s\n",
     txmsg->cur_offset, txmsg->cur_len, txmsg->seqno,
     drm_dp_mst_sideband_tx_state_str(txmsg->state),
     txmsg->path_msg, buf);

 ret = drm_dp_decode_sideband_req(txmsg, &req);
 if (ret) {
  drm_printf(p, "\n", ret);
  return;
 }
 drm_dp_dump_sideband_msg_req_body(&req, 1, p);

 switch (req.req_type) {
 case DP_REMOTE_DPCD_WRITE:
  kfree(req.u.dpcd_write.bytes);
  break;
 case DP_REMOTE_I2C_READ:
  for (i = 0; i < req.u.i2c_read.num_transactions; i++)
   kfree(req.u.i2c_read.transactions[i].bytes);
  break;
 case DP_REMOTE_I2C_WRITE:
  kfree(req.u.i2c_write.bytes);
  break;
 }
}

static void drm_dp_crc_sideband_chunk_req(u8 *msg, u8 len)
{
 u8 crc4;

 crc4 = drm_dp_msg_data_crc4(msg, len);
 msg[len] = crc4;
}

static void drm_dp_encode_sideband_reply(struct drm_dp_sideband_msg_reply_body *rep,
      struct drm_dp_sideband_msg_tx *raw)
{
 int idx = 0;
 u8 *buf = raw->msg;

 buf[idx++] = (rep->reply_type & 0x1) << 7 | (rep->req_type & 0x7f);

 raw->cur_len = idx;
}

static int drm_dp_sideband_msg_set_header(struct drm_dp_sideband_msg_rx *msg,
       struct drm_dp_sideband_msg_hdr *hdr,
       u8 hdrlen)
{
 /*
 * ignore out-of-order messages or messages that are part of a
 * failed transaction
 */

 if (!hdr->somt && !msg->have_somt)
  return false;

 /* get length contained in this portion */
 msg->curchunk_idx = 0;
 msg->curchunk_len = hdr->msg_len;
 msg->curchunk_hdrlen = hdrlen;

 /* we have already gotten an somt - don't bother parsing */
 if (hdr->somt && msg->have_somt)
  return false;

 if (hdr->somt) {
  memcpy(&msg->initial_hdr, hdr,
         sizeof(struct drm_dp_sideband_msg_hdr));
  msg->have_somt = true;
 }
 if (hdr->eomt)
  msg->have_eomt = true;

 return true;
}

/* this adds a chunk of msg to the builder to get the final msg */
static bool drm_dp_sideband_append_payload(struct drm_dp_sideband_msg_rx *msg,
        u8 *replybuf, u8 replybuflen)
{
 u8 crc4;

 memcpy(&msg->chunk[msg->curchunk_idx], replybuf, replybuflen);
 msg->curchunk_idx += replybuflen;

 if (msg->curchunk_idx >= msg->curchunk_len) {
  /* do CRC */
  crc4 = drm_dp_msg_data_crc4(msg->chunk, msg->curchunk_len - 1);
  if (crc4 != msg->chunk[msg->curchunk_len - 1])
   print_hex_dump(KERN_DEBUG, "wrong crc",
           DUMP_PREFIX_NONE, 16, 1,
           msg->chunk,  msg->curchunk_len, false);
  /* copy chunk into bigger msg */
  memcpy(&msg->msg[msg->curlen], msg->chunk, msg->curchunk_len - 1);
  msg->curlen += msg->curchunk_len - 1;
 }
 return true;
}

static bool drm_dp_sideband_parse_link_address(const struct drm_dp_mst_topology_mgr *mgr,
            struct drm_dp_sideband_msg_rx *raw,
            struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;
 int i;

 import_guid(&repmsg->u.link_addr.guid, &raw->msg[idx]);
 idx += 16;
 repmsg->u.link_addr.nports = raw->msg[idx] & 0xf;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 for (i = 0; i < repmsg->u.link_addr.nports; i++) {
  if (raw->msg[idx] & 0x80)
   repmsg->u.link_addr.ports[i].input_port = 1;

  repmsg->u.link_addr.ports[i].peer_device_type = (raw->msg[idx] >> 4) & 0x7;
  repmsg->u.link_addr.ports[i].port_number = (raw->msg[idx] & 0xf);

  idx++;
  if (idx > raw->curlen)
   goto fail_len;
  repmsg->u.link_addr.ports[i].mcs = (raw->msg[idx] >> 7) & 0x1;
  repmsg->u.link_addr.ports[i].ddps = (raw->msg[idx] >> 6) & 0x1;
  if (repmsg->u.link_addr.ports[i].input_port == 0)
   repmsg->u.link_addr.ports[i].legacy_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
  idx++;
  if (idx > raw->curlen)
   goto fail_len;
  if (repmsg->u.link_addr.ports[i].input_port == 0) {
   repmsg->u.link_addr.ports[i].dpcd_revision = (raw->msg[idx]);
   idx++;
   if (idx > raw->curlen)
    goto fail_len;
   import_guid(&repmsg->u.link_addr.ports[i].peer_guid, &raw->msg[idx]);
   idx += 16;
   if (idx > raw->curlen)
    goto fail_len;
   repmsg->u.link_addr.ports[i].num_sdp_streams = (raw->msg[idx] >> 4) & 0xf;
   repmsg->u.link_addr.ports[i].num_sdp_stream_sinks = (raw->msg[idx] & 0xf);
   idx++;

  }
  if (idx > raw->curlen)
   goto fail_len;
 }

 return true;
fail_len:
 DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_remote_dpcd_read(struct drm_dp_sideband_msg_rx *raw,
         struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.remote_dpcd_read_ack.port_number = raw->msg[idx] & 0xf;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.remote_dpcd_read_ack.num_bytes = raw->msg[idx];
 idx++;
 if (idx > raw->curlen)
  goto fail_len;

 memcpy(repmsg->u.remote_dpcd_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_dpcd_read_ack.num_bytes);
 return true;
fail_len:
 DRM_DEBUG_KMS("link address reply parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_remote_dpcd_write(struct drm_dp_sideband_msg_rx *raw,
            struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.remote_dpcd_write_ack.port_number = raw->msg[idx] & 0xf;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 return true;
fail_len:
 DRM_DEBUG_KMS("parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_remote_i2c_read_ack(struct drm_dp_sideband_msg_rx *raw,
            struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.remote_i2c_read_ack.port_number = (raw->msg[idx] & 0xf);
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.remote_i2c_read_ack.num_bytes = raw->msg[idx];
 idx++;
 /* TODO check */
 memcpy(repmsg->u.remote_i2c_read_ack.bytes, &raw->msg[idx], repmsg->u.remote_i2c_read_ack.num_bytes);
 return true;
fail_len:
 DRM_DEBUG_KMS("remote i2c reply parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_enum_path_resources_ack(struct drm_dp_sideband_msg_rx *raw,
         struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.path_resources.port_number = (raw->msg[idx] >> 4) & 0xf;
 repmsg->u.path_resources.fec_capable = raw->msg[idx] & 0x1;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.path_resources.full_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
 idx += 2;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.path_resources.avail_payload_bw_number = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
 idx += 2;
 if (idx > raw->curlen)
  goto fail_len;
 return true;
fail_len:
 DRM_DEBUG_KMS("enum resource parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_allocate_payload_ack(struct drm_dp_sideband_msg_rx *raw,
         struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.allocate_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.allocate_payload.vcpi = raw->msg[idx];
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.allocate_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx+1]);
 idx += 2;
 if (idx > raw->curlen)
  goto fail_len;
 return true;
fail_len:
 DRM_DEBUG_KMS("allocate payload parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_query_payload_ack(struct drm_dp_sideband_msg_rx *raw,
          struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.query_payload.port_number = (raw->msg[idx] >> 4) & 0xf;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;
 repmsg->u.query_payload.allocated_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
 idx += 2;
 if (idx > raw->curlen)
  goto fail_len;
 return true;
fail_len:
 DRM_DEBUG_KMS("query payload parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_power_updown_phy_ack(struct drm_dp_sideband_msg_rx *raw,
             struct drm_dp_sideband_msg_reply_body *repmsg)
{
 int idx = 1;

 repmsg->u.port_number.port_number = (raw->msg[idx] >> 4) & 0xf;
 idx++;
 if (idx > raw->curlen) {
  DRM_DEBUG_KMS("power up/down phy parse length fail %d %d\n",
         idx, raw->curlen);
  return false;
 }
 return true;
}

static bool
drm_dp_sideband_parse_query_stream_enc_status(
    struct drm_dp_sideband_msg_rx *raw,
    struct drm_dp_sideband_msg_reply_body *repmsg)
{
 struct drm_dp_query_stream_enc_status_ack_reply *reply;

 reply = &repmsg->u.enc_status;

 reply->stream_id = raw->msg[3];

 reply->reply_signed = raw->msg[2] & BIT(0);

 /*
 * NOTE: It's my impression from reading the spec that the below parsing
 * is correct. However I noticed while testing with an HDCP 1.4 display
 * through an HDCP 2.2 hub that only bit 3 was set. In that case, I
 * would expect both bits to be set. So keep the parsing following the
 * spec, but beware reality might not match the spec (at least for some
 * configurations).
 */

 reply->hdcp_1x_device_present = raw->msg[2] & BIT(4);
 reply->hdcp_2x_device_present = raw->msg[2] & BIT(3);

 reply->query_capable_device_present = raw->msg[2] & BIT(5);
 reply->legacy_device_present = raw->msg[2] & BIT(6);
 reply->unauthorizable_device_present = raw->msg[2] & BIT(7);

 reply->auth_completed = !!(raw->msg[1] & BIT(3));
 reply->encryption_enabled = !!(raw->msg[1] & BIT(4));
 reply->repeater_present = !!(raw->msg[1] & BIT(5));
 reply->state = (raw->msg[1] & GENMASK(7, 6)) >> 6;

 return true;
}

static bool drm_dp_sideband_parse_reply(const struct drm_dp_mst_topology_mgr *mgr,
     struct drm_dp_sideband_msg_rx *raw,
     struct drm_dp_sideband_msg_reply_body *msg)
{
 memset(msg, 0, sizeof(*msg));
 msg->reply_type = (raw->msg[0] & 0x80) >> 7;
 msg->req_type = (raw->msg[0] & 0x7f);

 if (msg->reply_type == DP_SIDEBAND_REPLY_NAK) {
  import_guid(&msg->u.nak.guid, &raw->msg[1]);
  msg->u.nak.reason = raw->msg[17];
  msg->u.nak.nak_data = raw->msg[18];
  return false;
 }

 switch (msg->req_type) {
 case DP_LINK_ADDRESS:
  return drm_dp_sideband_parse_link_address(mgr, raw, msg);
 case DP_QUERY_PAYLOAD:
  return drm_dp_sideband_parse_query_payload_ack(raw, msg);
 case DP_REMOTE_DPCD_READ:
  return drm_dp_sideband_parse_remote_dpcd_read(raw, msg);
 case DP_REMOTE_DPCD_WRITE:
  return drm_dp_sideband_parse_remote_dpcd_write(raw, msg);
 case DP_REMOTE_I2C_READ:
  return drm_dp_sideband_parse_remote_i2c_read_ack(raw, msg);
 case DP_REMOTE_I2C_WRITE:
  return true/* since there's nothing to parse */
 case DP_ENUM_PATH_RESOURCES:
  return drm_dp_sideband_parse_enum_path_resources_ack(raw, msg);
 case DP_ALLOCATE_PAYLOAD:
  return drm_dp_sideband_parse_allocate_payload_ack(raw, msg);
 case DP_POWER_DOWN_PHY:
 case DP_POWER_UP_PHY:
  return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
 case DP_CLEAR_PAYLOAD_ID_TABLE:
  return true/* since there's nothing to parse */
 case DP_QUERY_STREAM_ENC_STATUS:
  return drm_dp_sideband_parse_query_stream_enc_status(raw, msg);
 default:
  drm_err(mgr->dev, "Got unknown reply 0x%02x (%s)\n",
   msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
  return false;
 }
}

static bool
drm_dp_sideband_parse_connection_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
            struct drm_dp_sideband_msg_rx *raw,
            struct drm_dp_sideband_msg_req_body *msg)
{
 int idx = 1;

 msg->u.conn_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;

 import_guid(&msg->u.conn_stat.guid, &raw->msg[idx]);
 idx += 16;
 if (idx > raw->curlen)
  goto fail_len;

 msg->u.conn_stat.legacy_device_plug_status = (raw->msg[idx] >> 6) & 0x1;
 msg->u.conn_stat.displayport_device_plug_status = (raw->msg[idx] >> 5) & 0x1;
 msg->u.conn_stat.message_capability_status = (raw->msg[idx] >> 4) & 0x1;
 msg->u.conn_stat.input_port = (raw->msg[idx] >> 3) & 0x1;
 msg->u.conn_stat.peer_device_type = (raw->msg[idx] & 0x7);
 idx++;
 return true;
fail_len:
 drm_dbg_kms(mgr->dev, "connection status reply parse length fail %d %d\n",
      idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_resource_status_notify(const struct drm_dp_mst_topology_mgr *mgr,
        struct drm_dp_sideband_msg_rx *raw,
        struct drm_dp_sideband_msg_req_body *msg)
{
 int idx = 1;

 msg->u.resource_stat.port_number = (raw->msg[idx] & 0xf0) >> 4;
 idx++;
 if (idx > raw->curlen)
  goto fail_len;

 import_guid(&msg->u.resource_stat.guid, &raw->msg[idx]);
 idx += 16;
 if (idx > raw->curlen)
  goto fail_len;

 msg->u.resource_stat.available_pbn = (raw->msg[idx] << 8) | (raw->msg[idx + 1]);
 idx++;
 return true;
fail_len:
 drm_dbg_kms(mgr->dev, "resource status reply parse length fail %d %d\n", idx, raw->curlen);
 return false;
}

static bool drm_dp_sideband_parse_req(const struct drm_dp_mst_topology_mgr *mgr,
          struct drm_dp_sideband_msg_rx *raw,
          struct drm_dp_sideband_msg_req_body *msg)
{
 memset(msg, 0, sizeof(*msg));
 msg->req_type = (raw->msg[0] & 0x7f);

 switch (msg->req_type) {
 case DP_CONNECTION_STATUS_NOTIFY:
  return drm_dp_sideband_parse_connection_status_notify(mgr, raw, msg);
 case DP_RESOURCE_STATUS_NOTIFY:
  return drm_dp_sideband_parse_resource_status_notify(mgr, raw, msg);
 default:
  drm_err(mgr->dev, "Got unknown request 0x%02x (%s)\n",
   msg->req_type, drm_dp_mst_req_type_str(msg->req_type));
  return false;
 }
}

static void build_dpcd_write(struct drm_dp_sideband_msg_tx *msg,
        u8 port_num, u32 offset, u8 num_bytes, u8 *bytes)
{
 struct drm_dp_sideband_msg_req_body req;

 req.req_type = DP_REMOTE_DPCD_WRITE;
 req.u.dpcd_write.port_number = port_num;
 req.u.dpcd_write.dpcd_address = offset;
 req.u.dpcd_write.num_bytes = num_bytes;
 req.u.dpcd_write.bytes = bytes;
 drm_dp_encode_sideband_req(&req, msg);
}

static void build_link_address(struct drm_dp_sideband_msg_tx *msg)
{
 struct drm_dp_sideband_msg_req_body req;

 req.req_type = DP_LINK_ADDRESS;
 drm_dp_encode_sideband_req(&req, msg);
}

static void build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg)
{
 struct drm_dp_sideband_msg_req_body req;

 req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE;
 drm_dp_encode_sideband_req(&req, msg);
 msg->path_msg = true;
}

static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg,
         int port_num)
{
 struct drm_dp_sideband_msg_req_body req;

 req.req_type = DP_ENUM_PATH_RESOURCES;
 req.u.port_num.port_number = port_num;
 drm_dp_encode_sideband_req(&req, msg);
 msg->path_msg = true;
 return 0;
}

static void 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)
{
 struct drm_dp_sideband_msg_req_body req;

 memset(&req, 0, sizeof(req));
 req.req_type = DP_ALLOCATE_PAYLOAD;
 req.u.allocate_payload.port_number = port_num;
 req.u.allocate_payload.vcpi = vcpi;
 req.u.allocate_payload.pbn = pbn;
 req.u.allocate_payload.number_sdp_streams = number_sdp_streams;
 memcpy(req.u.allocate_payload.sdp_stream_sink, sdp_stream_sink,
     number_sdp_streams);
 drm_dp_encode_sideband_req(&req, msg);
 msg->path_msg = true;
}

static void build_power_updown_phy(struct drm_dp_sideband_msg_tx *msg,
       int port_num, bool power_up)
{
 struct drm_dp_sideband_msg_req_body req;

 if (power_up)
  req.req_type = DP_POWER_UP_PHY;
 else
  req.req_type = DP_POWER_DOWN_PHY;

 req.u.port_num.port_number = port_num;
 drm_dp_encode_sideband_req(&req, msg);
 msg->path_msg = true;
}

static int
build_query_stream_enc_status(struct drm_dp_sideband_msg_tx *msg, u8 stream_id,
         u8 *q_id)
{
 struct drm_dp_sideband_msg_req_body req;

 req.req_type = DP_QUERY_STREAM_ENC_STATUS;
 req.u.enc_status.stream_id = stream_id;
 memcpy(req.u.enc_status.client_id, q_id,
        sizeof(req.u.enc_status.client_id));
 req.u.enc_status.stream_event = 0;
 req.u.enc_status.valid_stream_event = false;
 req.u.enc_status.stream_behavior = 0;
 req.u.enc_status.valid_stream_behavior = false;

 drm_dp_encode_sideband_req(&req, msg);
 return 0;
}

static bool check_txmsg_state(struct drm_dp_mst_topology_mgr *mgr,
         struct drm_dp_sideband_msg_tx *txmsg)
{
 unsigned int state;

 /*
 * All updates to txmsg->state are protected by mgr->qlock, and the two
 * cases we check here are terminal states. For those the barriers
 * provided by the wake_up/wait_event pair are enough.
 */

 state = READ_ONCE(txmsg->state);
 return (state == DRM_DP_SIDEBAND_TX_RX ||
  state == DRM_DP_SIDEBAND_TX_TIMEOUT);
}

static int drm_dp_mst_wait_tx_reply(struct drm_dp_mst_branch *mstb,
        struct drm_dp_sideband_msg_tx *txmsg)
{
 struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;
 unsigned long wait_timeout = msecs_to_jiffies(4000);
 unsigned long wait_expires = jiffies + wait_timeout;
 int ret;

 for (;;) {
  /*
 * If the driver provides a way for this, change to
 * poll-waiting for the MST reply interrupt if we didn't receive
 * it for 50 msec. This would cater for cases where the HPD
 * pulse signal got lost somewhere, even though the sink raised
 * the corresponding MST interrupt correctly. One example is the
 * Club 3D CAC-1557 TypeC -> DP adapter which for some reason
 * filters out short pulses with a duration less than ~540 usec.
 *
 * The poll period is 50 msec to avoid missing an interrupt
 * after the sink has cleared it (after a 110msec timeout
 * since it raised the interrupt).
 */

  ret = wait_event_timeout(mgr->tx_waitq,
      check_txmsg_state(mgr, txmsg),
      mgr->cbs->poll_hpd_irq ?
      msecs_to_jiffies(50) :
      wait_timeout);

  if (ret || !mgr->cbs->poll_hpd_irq ||
      time_after(jiffies, wait_expires))
   break;

  mgr->cbs->poll_hpd_irq(mgr);
 }

 mutex_lock(&mgr->qlock);
 if (ret > 0) {
  if (txmsg->state == DRM_DP_SIDEBAND_TX_TIMEOUT) {
   ret = -EIO;
   goto out;
  }
 } else {
  drm_dbg_kms(mgr->dev, "timedout msg send %p %d %d\n",
       txmsg, txmsg->state, txmsg->seqno);

  /* dump some state */
  ret = -EIO;

  /* remove from q */
  if (txmsg->state == DRM_DP_SIDEBAND_TX_QUEUED ||
      txmsg->state == DRM_DP_SIDEBAND_TX_START_SEND ||
      txmsg->state == DRM_DP_SIDEBAND_TX_SENT)
   list_del(&txmsg->next);
 }
out:
 if (unlikely(ret == -EIO) && drm_debug_enabled(DRM_UT_DP)) {
  struct drm_printer p = drm_dbg_printer(mgr->dev, DRM_UT_DP,
             DBG_PREFIX);

  drm_dp_mst_dump_sideband_msg_tx(&p, txmsg);
 }
 mutex_unlock(&mgr->qlock);

 drm_dp_mst_kick_tx(mgr);
 return ret;
}

static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
{
 struct drm_dp_mst_branch *mstb;

 mstb = kzalloc(sizeof(*mstb), GFP_KERNEL);
 if (!mstb)
  return NULL;

 mstb->lct = lct;
 if (lct > 1)
  memcpy(mstb->rad, rad, lct / 2);
 INIT_LIST_HEAD(&mstb->ports);
 kref_init(&mstb->topology_kref);
 kref_init(&mstb->malloc_kref);
 return mstb;
}

static void drm_dp_free_mst_branch_device(struct kref *kref)
{
 struct drm_dp_mst_branch *mstb =
  container_of(kref, struct drm_dp_mst_branch, malloc_kref);

 if (mstb->port_parent)
  drm_dp_mst_put_port_malloc(mstb->port_parent);

 kfree(mstb);
}

/**
 * DOC: Branch device and port refcounting
 *
 * Topology refcount overview
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * The refcounting schemes for &struct drm_dp_mst_branch and &struct
 * drm_dp_mst_port are somewhat unusual. Both ports and branch devices have
 * two different kinds of refcounts: topology refcounts, and malloc refcounts.
 *
 * Topology refcounts are not exposed to drivers, and are handled internally
 * by the DP MST helpers. The helpers use them in order to prevent the
 * in-memory topology state from being changed in the middle of critical
 * operations like changing the internal state of payload allocations. This
 * means each branch and port will be considered to be connected to the rest
 * of the topology until its topology refcount reaches zero. Additionally,
 * for ports this means that their associated &struct drm_connector will stay
 * registered with userspace until the port's refcount reaches 0.
 *
 * Malloc refcount overview
 * ~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Malloc references are used to keep a &struct drm_dp_mst_port or &struct
 * drm_dp_mst_branch allocated even after all of its topology references have
 * been dropped, so that the driver or MST helpers can safely access each
 * branch's last known state before it was disconnected from the topology.
 * When the malloc refcount of a port or branch reaches 0, the memory
 * allocation containing the &struct drm_dp_mst_branch or &struct
 * drm_dp_mst_port respectively will be freed.
 *
 * For &struct drm_dp_mst_branch, malloc refcounts are not currently exposed
 * to drivers. As of writing this documentation, there are no drivers that
 * have a usecase for accessing &struct drm_dp_mst_branch outside of the MST
 * helpers. Exposing this API to drivers in a race-free manner would take more
 * tweaking of the refcounting scheme, however patches are welcome provided
 * there is a legitimate driver usecase for this.
 *
 * Refcount relationships in a topology
 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *
 * Let's take a look at why the relationship between topology and malloc
 * refcounts is designed the way it is.
 *
 * .. kernel-figure:: dp-mst/topology-figure-1.dot
 *
 *    An example of topology and malloc refs in a DP MST topology with two
 *    active payloads. Topology refcount increments are indicated by solid
 *    lines, and malloc refcount increments are indicated by dashed lines.
 *    Each starts from the branch which incremented the refcount, and ends at
 *    the branch to which the refcount belongs to, i.e. the arrow points the
 *    same way as the C pointers used to reference a structure.
 *
 * As you can see in the above figure, every branch increments the topology
 * refcount of its children, and increments the malloc refcount of its
 * parent. Additionally, every payload increments the malloc refcount of its
 * assigned port by 1.
 *
 * So, what would happen if MSTB #3 from the above figure was unplugged from
 * the system, but the driver hadn't yet removed payload #2 from port #3? The
 * topology would start to look like the figure below.
 *
 * .. kernel-figure:: dp-mst/topology-figure-2.dot
 *
 *    Ports and branch devices which have been released from memory are
 *    colored grey, and references which have been removed are colored red.
 *
 * Whenever a port or branch device's topology refcount reaches zero, it will
 * decrement the topology refcounts of all its children, the malloc refcount
 * of its parent, and finally its own malloc refcount. For MSTB #4 and port
 * #4, this means they both have been disconnected from the topology and freed
 * from memory. But, because payload #2 is still holding a reference to port
 * #3, port #3 is removed from the topology but its &struct drm_dp_mst_port
 * is still accessible from memory. This also means port #3 has not yet
 * decremented the malloc refcount of MSTB #3, so its &struct
 * drm_dp_mst_branch will also stay allocated in memory until port #3's
 * malloc refcount reaches 0.
 *
 * This relationship is necessary because in order to release payload #2, we
 * need to be able to figure out the last relative of port #3 that's still
 * connected to the topology. In this case, we would travel up the topology as
 * shown below.
 *
 * .. kernel-figure:: dp-mst/topology-figure-3.dot
 *
 * And finally, remove payload #2 by communicating with port #2 through
 * sideband transactions.
 */


/**
 * drm_dp_mst_get_mstb_malloc() - Increment the malloc refcount of a branch
 * device
 * @mstb: The &struct drm_dp_mst_branch to increment the malloc refcount of
 *
 * Increments &drm_dp_mst_branch.malloc_kref. When
 * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
 * will be released and @mstb may no longer be used.
 *
 * See also: drm_dp_mst_put_mstb_malloc()
 */

static void
drm_dp_mst_get_mstb_malloc(struct drm_dp_mst_branch *mstb)
{
 kref_get(&mstb->malloc_kref);
 drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref));
}

/**
 * drm_dp_mst_put_mstb_malloc() - Decrement the malloc refcount of a branch
 * device
 * @mstb: The &struct drm_dp_mst_branch to decrement the malloc refcount of
 *
 * Decrements &drm_dp_mst_branch.malloc_kref. When
 * &drm_dp_mst_branch.malloc_kref reaches 0, the memory allocation for @mstb
 * will be released and @mstb may no longer be used.
 *
 * See also: drm_dp_mst_get_mstb_malloc()
 */

static void
drm_dp_mst_put_mstb_malloc(struct drm_dp_mst_branch *mstb)
{
 drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->malloc_kref) - 1);
 kref_put(&mstb->malloc_kref, drm_dp_free_mst_branch_device);
}

static void drm_dp_free_mst_port(struct kref *kref)
{
 struct drm_dp_mst_port *port =
  container_of(kref, struct drm_dp_mst_port, malloc_kref);

 drm_dp_mst_put_mstb_malloc(port->parent);
 kfree(port);
}

/**
 * drm_dp_mst_get_port_malloc() - Increment the malloc refcount of an MST port
 * @port: The &struct drm_dp_mst_port to increment the malloc refcount of
 *
 * Increments &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
 * reaches 0, the memory allocation for @port will be released and @port may
 * no longer be used.
 *
 * Because @port could potentially be freed at any time by the DP MST helpers
 * if &drm_dp_mst_port.malloc_kref reaches 0, including during a call to this
 * function, drivers that which to make use of &struct drm_dp_mst_port should
 * ensure that they grab at least one main malloc reference to their MST ports
 * in &drm_dp_mst_topology_cbs.add_connector. This callback is called before
 * there is any chance for &drm_dp_mst_port.malloc_kref to reach 0.
 *
 * See also: drm_dp_mst_put_port_malloc()
 */

void
drm_dp_mst_get_port_malloc(struct drm_dp_mst_port *port)
{
 kref_get(&port->malloc_kref);
 drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref));
}
EXPORT_SYMBOL(drm_dp_mst_get_port_malloc);

/**
 * drm_dp_mst_put_port_malloc() - Decrement the malloc refcount of an MST port
 * @port: The &struct drm_dp_mst_port to decrement the malloc refcount of
 *
 * Decrements &drm_dp_mst_port.malloc_kref. When &drm_dp_mst_port.malloc_kref
 * reaches 0, the memory allocation for @port will be released and @port may
 * no longer be used.
 *
 * See also: drm_dp_mst_get_port_malloc()
 */

void
drm_dp_mst_put_port_malloc(struct drm_dp_mst_port *port)
{
 drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->malloc_kref) - 1);
 kref_put(&port->malloc_kref, drm_dp_free_mst_port);
}
EXPORT_SYMBOL(drm_dp_mst_put_port_malloc);

#if IS_ENABLED(CONFIG_DRM_DEBUG_DP_MST_TOPOLOGY_REFS)

#define STACK_DEPTH 8

static noinline void
__topology_ref_save(struct drm_dp_mst_topology_mgr *mgr,
      struct drm_dp_mst_topology_ref_history *history,
      enum drm_dp_mst_topology_ref_type type)
{
 struct drm_dp_mst_topology_ref_entry *entry = NULL;
 depot_stack_handle_t backtrace;
 ulong stack_entries[STACK_DEPTH];
 uint n;
 int i;

 n = stack_trace_save(stack_entries, ARRAY_SIZE(stack_entries), 1);
 backtrace = stack_depot_save(stack_entries, n, GFP_KERNEL);
 if (!backtrace)
  return;

 /* Try to find an existing entry for this backtrace */
 for (i = 0; i < history->len; i++) {
  if (history->entries[i].backtrace == backtrace) {
   entry = &history->entries[i];
   break;
  }
 }

 /* Otherwise add one */
 if (!entry) {
  struct drm_dp_mst_topology_ref_entry *new;
  int new_len = history->len + 1;

  new = krealloc(history->entries, sizeof(*new) * new_len,
          GFP_KERNEL);
  if (!new)
   return;

  entry = &new[history->len];
  history->len = new_len;
  history->entries = new;

  entry->backtrace = backtrace;
  entry->type = type;
  entry->count = 0;
 }
 entry->count++;
 entry->ts_nsec = ktime_get_ns();
}

static int
topology_ref_history_cmp(const void *a, const void *b)
{
 const struct drm_dp_mst_topology_ref_entry *entry_a = a, *entry_b = b;

 if (entry_a->ts_nsec > entry_b->ts_nsec)
  return 1;
 else if (entry_a->ts_nsec < entry_b->ts_nsec)
  return -1;
 else
  return 0;
}

static inline const char *
topology_ref_type_to_str(enum drm_dp_mst_topology_ref_type type)
{
 if (type == DRM_DP_MST_TOPOLOGY_REF_GET)
  return "get";
 else
  return "put";
}

static void
__dump_topology_ref_history(struct drm_device *drm,
       struct drm_dp_mst_topology_ref_history *history,
       void *ptr, const char *type_str)
{
 struct drm_printer p = drm_dbg_printer(drm, DRM_UT_DP, DBG_PREFIX);
 char *buf = kzalloc(PAGE_SIZE, GFP_KERNEL);
 int i;

 if (!buf)
  return;

 if (!history->len)
  goto out;

 /* First, sort the list so that it goes from oldest to newest
 * reference entry
 */

 sort(history->entries, history->len, sizeof(*history->entries),
      topology_ref_history_cmp, NULL);

 drm_printf(&p, "%s (%p) topology count reached 0, dumping history:\n",
     type_str, ptr);

 for (i = 0; i < history->len; i++) {
  const struct drm_dp_mst_topology_ref_entry *entry =
   &history->entries[i];
  u64 ts_nsec = entry->ts_nsec;
  u32 rem_nsec = do_div(ts_nsec, 1000000000);

  stack_depot_snprint(entry->backtrace, buf, PAGE_SIZE, 4);

  drm_printf(&p, " %d %ss (last at %5llu.%06u):\n%s",
      entry->count,
      topology_ref_type_to_str(entry->type),
      ts_nsec, rem_nsec / 1000, buf);
 }

 /* Now free the history, since this is the only time we expose it */
 kfree(history->entries);
out:
 kfree(buf);
}

static __always_inline void
drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb)
{
 __dump_topology_ref_history(mstb->mgr->dev, &mstb->topology_ref_history,
        mstb, "MSTB");
}

static __always_inline void
drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port)
{
 __dump_topology_ref_history(port->mgr->dev, &port->topology_ref_history,
        port, "Port");
}

static __always_inline void
save_mstb_topology_ref(struct drm_dp_mst_branch *mstb,
         enum drm_dp_mst_topology_ref_type type)
{
 __topology_ref_save(mstb->mgr, &mstb->topology_ref_history, type);
}

static __always_inline void
save_port_topology_ref(struct drm_dp_mst_port *port,
         enum drm_dp_mst_topology_ref_type type)
{
 __topology_ref_save(port->mgr, &port->topology_ref_history, type);
}

static inline void
topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr)
{
 mutex_lock(&mgr->topology_ref_history_lock);
}

static inline void
topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr)
{
 mutex_unlock(&mgr->topology_ref_history_lock);
}
#else
static inline void
topology_ref_history_lock(struct drm_dp_mst_topology_mgr *mgr) {}
static inline void
topology_ref_history_unlock(struct drm_dp_mst_topology_mgr *mgr) {}
static inline void
drm_dp_mst_dump_mstb_topology_history(struct drm_dp_mst_branch *mstb) {}
static inline void
drm_dp_mst_dump_port_topology_history(struct drm_dp_mst_port *port) {}
#define save_mstb_topology_ref(mstb, type)
#define save_port_topology_ref(port, type)
#endif

struct drm_dp_mst_atomic_payload *
drm_atomic_get_mst_payload_state(struct drm_dp_mst_topology_state *state,
     struct drm_dp_mst_port *port)
{
 struct drm_dp_mst_atomic_payload *payload;

 list_for_each_entry(payload, &state->payloads, next)
  if (payload->port == port)
   return payload;

 return NULL;
}
EXPORT_SYMBOL(drm_atomic_get_mst_payload_state);

static void drm_dp_destroy_mst_branch_device(struct kref *kref)
{
 struct drm_dp_mst_branch *mstb =
  container_of(kref, struct drm_dp_mst_branch, topology_kref);
 struct drm_dp_mst_topology_mgr *mgr = mstb->mgr;

 drm_dp_mst_dump_mstb_topology_history(mstb);

 INIT_LIST_HEAD(&mstb->destroy_next);

 /*
 * This can get called under mgr->mutex, so we need to perform the
 * actual destruction of the mstb in another worker
 */

 mutex_lock(&mgr->delayed_destroy_lock);
 list_add(&mstb->destroy_next, &mgr->destroy_branch_device_list);
 mutex_unlock(&mgr->delayed_destroy_lock);
 queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
}

/**
 * drm_dp_mst_topology_try_get_mstb() - Increment the topology refcount of a
 * branch device unless it's zero
 * @mstb: &struct drm_dp_mst_branch to increment the topology refcount of
 *
 * Attempts to grab a topology reference to @mstb, if it hasn't yet been
 * removed from the topology (e.g. &drm_dp_mst_branch.topology_kref has
 * reached 0). Holding a topology reference implies that a malloc reference
 * will be held to @mstb as long as the user holds the topology reference.
 *
 * Care should be taken to ensure that the user has at least one malloc
 * reference to @mstb. If you already have a topology reference to @mstb, you
 * should use drm_dp_mst_topology_get_mstb() instead.
 *
 * See also:
 * drm_dp_mst_topology_get_mstb()
 * drm_dp_mst_topology_put_mstb()
 *
 * Returns:
 * * 1: A topology reference was grabbed successfully
 * * 0: @port is no longer in the topology, no reference was grabbed
 */

static int __must_check
drm_dp_mst_topology_try_get_mstb(struct drm_dp_mst_branch *mstb)
{
 int ret;

 topology_ref_history_lock(mstb->mgr);
 ret = kref_get_unless_zero(&mstb->topology_kref);
 if (ret) {
  drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));
  save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
 }

 topology_ref_history_unlock(mstb->mgr);

 return ret;
}

/**
 * drm_dp_mst_topology_get_mstb() - Increment the topology refcount of a
 * branch device
 * @mstb: The &struct drm_dp_mst_branch to increment the topology refcount of
 *
 * Increments &drm_dp_mst_branch.topology_refcount without checking whether or
 * not it's already reached 0. This is only valid to use in scenarios where
 * you are already guaranteed to have at least one active topology reference
 * to @mstb. Otherwise, drm_dp_mst_topology_try_get_mstb() must be used.
 *
 * See also:
 * drm_dp_mst_topology_try_get_mstb()
 * drm_dp_mst_topology_put_mstb()
 */

static void drm_dp_mst_topology_get_mstb(struct drm_dp_mst_branch *mstb)
{
 topology_ref_history_lock(mstb->mgr);

 save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_GET);
 WARN_ON(kref_read(&mstb->topology_kref) == 0);
 kref_get(&mstb->topology_kref);
 drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref));

 topology_ref_history_unlock(mstb->mgr);
}

/**
 * drm_dp_mst_topology_put_mstb() - release a topology reference to a branch
 * device
 * @mstb: The &struct drm_dp_mst_branch to release the topology reference from
 *
 * Releases a topology reference from @mstb by decrementing
 * &drm_dp_mst_branch.topology_kref.
 *
 * See also:
 * drm_dp_mst_topology_try_get_mstb()
 * drm_dp_mst_topology_get_mstb()
 */

static void
drm_dp_mst_topology_put_mstb(struct drm_dp_mst_branch *mstb)
{
 topology_ref_history_lock(mstb->mgr);

 drm_dbg(mstb->mgr->dev, "mstb %p (%d)\n", mstb, kref_read(&mstb->topology_kref) - 1);
 save_mstb_topology_ref(mstb, DRM_DP_MST_TOPOLOGY_REF_PUT);

 topology_ref_history_unlock(mstb->mgr);
 kref_put(&mstb->topology_kref, drm_dp_destroy_mst_branch_device);
}

static void drm_dp_destroy_port(struct kref *kref)
{
 struct drm_dp_mst_port *port =
  container_of(kref, struct drm_dp_mst_port, topology_kref);
 struct drm_dp_mst_topology_mgr *mgr = port->mgr;

 drm_dp_mst_dump_port_topology_history(port);

 /* There's nothing that needs locking to destroy an input port yet */
 if (port->input) {
  drm_dp_mst_put_port_malloc(port);
  return;
 }

 drm_edid_free(port->cached_edid);

 /*
 * we can't destroy the connector here, as we might be holding the
 * mode_config.mutex from an EDID retrieval
 */

 mutex_lock(&mgr->delayed_destroy_lock);
 list_add(&port->next, &mgr->destroy_port_list);
 mutex_unlock(&mgr->delayed_destroy_lock);
 queue_work(mgr->delayed_destroy_wq, &mgr->delayed_destroy_work);
}

/**
 * drm_dp_mst_topology_try_get_port() - Increment the topology refcount of a
 * port unless it's zero
 * @port: &struct drm_dp_mst_port to increment the topology refcount of
 *
 * Attempts to grab a topology reference to @port, if it hasn't yet been
 * removed from the topology (e.g. &drm_dp_mst_port.topology_kref has reached
 * 0). Holding a topology reference implies that a malloc reference will be
 * held to @port as long as the user holds the topology reference.
 *
 * Care should be taken to ensure that the user has at least one malloc
 * reference to @port. If you already have a topology reference to @port, you
 * should use drm_dp_mst_topology_get_port() instead.
 *
 * See also:
 * drm_dp_mst_topology_get_port()
 * drm_dp_mst_topology_put_port()
 *
 * Returns:
 * * 1: A topology reference was grabbed successfully
 * * 0: @port is no longer in the topology, no reference was grabbed
 */

static int __must_check
drm_dp_mst_topology_try_get_port(struct drm_dp_mst_port *port)
{
 int ret;

 topology_ref_history_lock(port->mgr);
 ret = kref_get_unless_zero(&port->topology_kref);
 if (ret) {
  drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
  save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);
 }

 topology_ref_history_unlock(port->mgr);
 return ret;
}

/**
 * drm_dp_mst_topology_get_port() - Increment the topology refcount of a port
 * @port: The &struct drm_dp_mst_port to increment the topology refcount of
 *
 * Increments &drm_dp_mst_port.topology_refcount without checking whether or
 * not it's already reached 0. This is only valid to use in scenarios where
 * you are already guaranteed to have at least one active topology reference
 * to @port. Otherwise, drm_dp_mst_topology_try_get_port() must be used.
 *
 * See also:
 * drm_dp_mst_topology_try_get_port()
 * drm_dp_mst_topology_put_port()
 */

static void drm_dp_mst_topology_get_port(struct drm_dp_mst_port *port)
{
 topology_ref_history_lock(port->mgr);

 WARN_ON(kref_read(&port->topology_kref) == 0);
 kref_get(&port->topology_kref);
 drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref));
 save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_GET);

 topology_ref_history_unlock(port->mgr);
}

/**
 * drm_dp_mst_topology_put_port() - release a topology reference to a port
 * @port: The &struct drm_dp_mst_port to release the topology reference from
 *
 * Releases a topology reference from @port by decrementing
 * &drm_dp_mst_port.topology_kref.
 *
 * See also:
 * drm_dp_mst_topology_try_get_port()
 * drm_dp_mst_topology_get_port()
 */

static void drm_dp_mst_topology_put_port(struct drm_dp_mst_port *port)
{
 topology_ref_history_lock(port->mgr);

 drm_dbg(port->mgr->dev, "port %p (%d)\n", port, kref_read(&port->topology_kref) - 1);
 save_port_topology_ref(port, DRM_DP_MST_TOPOLOGY_REF_PUT);

 topology_ref_history_unlock(port->mgr);
 kref_put(&port->topology_kref, drm_dp_destroy_port);
}

static struct drm_dp_mst_branch *
drm_dp_mst_topology_get_mstb_validated_locked(struct drm_dp_mst_branch *mstb,
           struct drm_dp_mst_branch *to_find)
{
 struct drm_dp_mst_port *port;
 struct drm_dp_mst_branch *rmstb;

 if (to_find == mstb)
  return mstb;

 list_for_each_entry(port, &mstb->ports, next) {
  if (port->mstb) {
   rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
       port->mstb, to_find);
   if (rmstb)
    return rmstb;
  }
 }
 return NULL;
}

static struct drm_dp_mst_branch *
drm_dp_mst_topology_get_mstb_validated(struct drm_dp_mst_topology_mgr *mgr,
           struct drm_dp_mst_branch *mstb)
{
 struct drm_dp_mst_branch *rmstb = NULL;

 mutex_lock(&mgr->lock);
 if (mgr->mst_primary) {
  rmstb = drm_dp_mst_topology_get_mstb_validated_locked(
      mgr->mst_primary, mstb);

  if (rmstb && !drm_dp_mst_topology_try_get_mstb(rmstb))
   rmstb = NULL;
 }
 mutex_unlock(&mgr->lock);
 return rmstb;
}

static struct drm_dp_mst_port *
drm_dp_mst_topology_get_port_validated_locked(struct drm_dp_mst_branch *mstb,
           struct drm_dp_mst_port *to_find)
{
 struct drm_dp_mst_port *port, *mport;

 list_for_each_entry(port, &mstb->ports, next) {
  if (port == to_find)
   return port;

  if (port->mstb) {
   mport = drm_dp_mst_topology_get_port_validated_locked(
       port->mstb, to_find);
   if (mport)
    return mport;
  }
 }
 return NULL;
}

static struct drm_dp_mst_port *
drm_dp_mst_topology_get_port_validated(struct drm_dp_mst_topology_mgr *mgr,
           struct drm_dp_mst_port *port)
{
 struct drm_dp_mst_port *rport = NULL;

 mutex_lock(&mgr->lock);
 if (mgr->mst_primary) {
  rport = drm_dp_mst_topology_get_port_validated_locked(
      mgr->mst_primary, port);

  if (rport && !drm_dp_mst_topology_try_get_port(rport))
   rport = NULL;
 }
 mutex_unlock(&mgr->lock);
 return rport;
}

static struct drm_dp_mst_port *drm_dp_get_port(struct drm_dp_mst_branch *mstb, u8 port_num)
{
 struct drm_dp_mst_port *port;
 int ret;

 list_for_each_entry(port, &mstb->ports, next) {
  if (port->port_num == port_num) {
   ret = drm_dp_mst_topology_try_get_port(port);
   return ret ? port : NULL;
  }
 }

 return NULL;
}

/*
 * calculate a new RAD for this MST branch device
 * if parent has an LCT of 2 then it has 1 nibble of RAD,
 * if parent has an LCT of 3 then it has 2 nibbles of RAD,
 */

static u8 drm_dp_calculate_rad(struct drm_dp_mst_port *port,
     u8 *rad)
{
 int parent_lct = port->parent->lct;
 int shift = 4;
 int idx = (parent_lct - 1) / 2;

 if (parent_lct > 1) {
  memcpy(rad, port->parent->rad, idx + 1);
  shift = (parent_lct % 2) ? 4 : 0;
 } else
  rad[0] = 0;

 rad[idx] |= port->port_num << shift;
 return parent_lct + 1;
}

static bool drm_dp_mst_is_end_device(u8 pdt, bool mcs)
{
 switch (pdt) {
 case DP_PEER_DEVICE_DP_LEGACY_CONV:
 case DP_PEER_DEVICE_SST_SINK:
  return true;
 case DP_PEER_DEVICE_MST_BRANCHING:
  /* For sst branch device */
  if (!mcs)
   return true;

  return false;
 }
 return true;
}

static int
drm_dp_port_set_pdt(struct drm_dp_mst_port *port, u8 new_pdt,
      bool new_mcs)
{
 struct drm_dp_mst_topology_mgr *mgr = port->mgr;
 struct drm_dp_mst_branch *mstb;
 u8 rad[8], lct;
 int ret = 0;

 if (port->pdt == new_pdt && port->mcs == new_mcs)
  return 0;

 /* Teardown the old pdt, if there is one */
 if (port->pdt != DP_PEER_DEVICE_NONE) {
  if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
   /*
 * If the new PDT would also have an i2c bus,
 * don't bother with reregistering it
 */

   if (new_pdt != DP_PEER_DEVICE_NONE &&
       drm_dp_mst_is_end_device(new_pdt, new_mcs)) {
    port->pdt = new_pdt;
    port->mcs = new_mcs;
    return 0;
   }

   /* remove i2c over sideband */
   drm_dp_mst_unregister_i2c_bus(port);
  } else {
   mutex_lock(&mgr->lock);
   drm_dp_mst_topology_put_mstb(port->mstb);
   port->mstb = NULL;
   mutex_unlock(&mgr->lock);
  }
 }

 port->pdt = new_pdt;
 port->mcs = new_mcs;

 if (port->pdt != DP_PEER_DEVICE_NONE) {
  if (drm_dp_mst_is_end_device(port->pdt, port->mcs)) {
   /* add i2c over sideband */
   ret = drm_dp_mst_register_i2c_bus(port);
  } else {
   lct = drm_dp_calculate_rad(port, rad);
   mstb = drm_dp_add_mst_branch_device(lct, rad);
   if (!mstb) {
    ret = -ENOMEM;
    drm_err(mgr->dev, "Failed to create MSTB for port %p", port);
    goto out;
   }

   mutex_lock(&mgr->lock);
   port->mstb = mstb;
   mstb->mgr = port->mgr;
   mstb->port_parent = port;

   /*
 * Make sure this port's memory allocation stays
 * around until its child MSTB releases it
 */

   drm_dp_mst_get_port_malloc(port);
   mutex_unlock(&mgr->lock);

   /* And make sure we send a link address for this */
   ret = 1;
  }
 }

out:
 if (ret < 0)
  port->pdt = DP_PEER_DEVICE_NONE;
 return ret;
}

/**
 * drm_dp_mst_dpcd_read() - read a series of bytes from the DPCD via sideband
 * @aux: Fake sideband AUX CH
 * @offset: address of the (first) register to read
 * @buffer: buffer to store the register values
 * @size: number of bytes in @buffer
 *
 * Performs the same functionality for remote devices via
 * sideband messaging as drm_dp_dpcd_read() does for local
 * devices via actual AUX CH.
 *
 * Return: Number of bytes read, or negative error code on failure.
 */

ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
        unsigned int offset, void *buffer, size_t size)
{
 struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
          aux);

 return drm_dp_send_dpcd_read(port->mgr, port,
         offset, size, buffer);
}

/**
 * drm_dp_mst_dpcd_write() - write a series of bytes to the DPCD via sideband
 * @aux: Fake sideband AUX CH
 * @offset: address of the (first) register to write
 * @buffer: buffer containing the values to write
 * @size: number of bytes in @buffer
 *
 * Performs the same functionality for remote devices via
 * sideband messaging as drm_dp_dpcd_write() does for local
 * devices via actual AUX CH.
 *
 * Return: number of bytes written on success, negative error code on failure.
 */

ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
         unsigned int offset, void *buffer, size_t size)
{
 struct drm_dp_mst_port *port = container_of(aux, struct drm_dp_mst_port,
          aux);

 return drm_dp_send_dpcd_write(port->mgr, port,
          offset, size, buffer);
}

static int drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, guid_t *guid)
{
 int ret = 0;

 guid_copy(&mstb->guid, guid);

 if (!drm_dp_validate_guid(mstb->mgr, &mstb->guid)) {
  struct drm_dp_aux *aux;
  u8 buf[UUID_SIZE];

  export_guid(buf, &mstb->guid);

  if (mstb->port_parent)
   aux = &mstb->port_parent->aux;
  else
   aux = mstb->mgr->aux;

  ret = drm_dp_dpcd_write_data(aux, DP_GUID, buf, sizeof(buf));
 }

 return ret;
}

static void build_mst_prop_path(const struct drm_dp_mst_branch *mstb,
    int pnum,
    char *proppath,
    size_t proppath_size)
{
 int i;
 char temp[8];

 snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id);
 for (i = 0; i < (mstb->lct - 1); i++) {
  int shift = (i % 2) ? 0 : 4;
  int port_num = (mstb->rad[i / 2] >> shift) & 0xf;

  snprintf(temp, sizeof(temp), "-%d", port_num);
  strlcat(proppath, temp, proppath_size);
 }
 snprintf(temp, sizeof(temp), "-%d", pnum);
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=91 G=92

¤ Dauer der Verarbeitung: 0.21 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.