[2/2] drm/dp_mst: Register aux-dev nodes for MST ports
diff mbox series

Message ID 1555085131-8716-3-git-send-email-sunpeng.li@amd.com
State New
Headers show
Series
  • Add AUX device entries for DP MST devices
Related show

Commit Message

Leo April 12, 2019, 4:05 p.m. UTC
From: Ville Syrjälä <ville.syrjala@linux.intel.com>

Expose AUX devices for MST ports, similar to how they are exposed for
SST.

The registered device will have it's MST port path appended in order to
identify it. i.e. /dev/drm_dp_aux4_mst:0-2-1

So for a MST topology like so:

               +---------+
               |  ASIC   |
               +---------+
              Conn-0|
                    |
               +----v----+
          +----| MST HUB |----+
          |    +---------+    |
          |                   |
          |Port-1       Port-2|
    +-----v-----+       +-----v-----+
    |  MST      |       |  SST      |
    |  Display  |       |  Display  |
    +-----------+       +-----------+
          |Port-1
          x

The list of AUX device names will look like:

AUX Device Name       | MST Device
----------------------+----------------------------------
drm_dp_aux0           | MST Hub
drm_dp_aux1_mst:0-1-1 | MST Display's disconnected DP out
drm_dp_aux2_mst:0-1   | MST Display
drm_dp_aux3_mst:0-2   | SST Display

Note that aux devices are only created for Physical Ports. Logical Ports
are left out, since they are internally connected within the MST device
(not connected to a DP RX or TX).

Leo Li:
* Add missing drm_crtc_helper_internal.h include
* Fix hard-coded offset and size in drm_dp_send_dpcd_read()
* Only create aux devices for physical ports.

Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
Signed-off-by: Leo Li <sunpeng.li@amd.com>
---
 drivers/gpu/drm/drm_dp_aux_dev.c      |  13 +++-
 drivers/gpu/drm/drm_dp_mst_topology.c | 109 ++++++++++++++++++++++++++++++----
 include/drm/drm_dp_helper.h           |   4 ++
 include/drm/drm_dp_mst_helper.h       |   6 ++
 4 files changed, 117 insertions(+), 15 deletions(-)

Comments

Lyude Paul April 16, 2019, 10:22 p.m. UTC | #1
On Fri, 2019-04-12 at 12:05 -0400, sunpeng.li@amd.com wrote:
> From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> 
> Expose AUX devices for MST ports, similar to how they are exposed for
> SST.
> 
> The registered device will have it's MST port path appended in order to
> identify it. i.e. /dev/drm_dp_aux4_mst:0-2-1
> 
> So for a MST topology like so:
> 
>                +---------+
>                |  ASIC   |
>                +---------+
>               Conn-0|
>                     |
>                +----v----+
>           +----| MST HUB |----+
>           |    +---------+    |
>           |                   |
>           |Port-1       Port-2|
>     +-----v-----+       +-----v-----+
>     |  MST      |       |  SST      |
>     |  Display  |       |  Display  |
>     +-----------+       +-----------+
>           |Port-1
>           x
> 
> The list of AUX device names will look like:
> 
> AUX Device Name       | MST Device
> ----------------------+----------------------------------
> drm_dp_aux0           | MST Hub
> drm_dp_aux1_mst:0-1-1 | MST Display's disconnected DP out
> drm_dp_aux2_mst:0-1   | MST Display
> drm_dp_aux3_mst:0-2   | SST Display
> 
> Note that aux devices are only created for Physical Ports. Logical Ports
> are left out, since they are internally connected within the MST device
> (not connected to a DP RX or TX).
> 
> Leo Li:
> * Add missing drm_crtc_helper_internal.h include
> * Fix hard-coded offset and size in drm_dp_send_dpcd_read()
> * Only create aux devices for physical ports.
> 
> Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> Signed-off-by: Leo Li <sunpeng.li@amd.com>
> ---
>  drivers/gpu/drm/drm_dp_aux_dev.c      |  13 +++-
>  drivers/gpu/drm/drm_dp_mst_topology.c | 109 ++++++++++++++++++++++++++++++-
> ---
>  include/drm/drm_dp_helper.h           |   4 ++
>  include/drm/drm_dp_mst_helper.h       |   6 ++
>  4 files changed, 117 insertions(+), 15 deletions(-)
> 
> diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c
> b/drivers/gpu/drm/drm_dp_aux_dev.c
> index 2310a67..f1241d1 100644
> --- a/drivers/gpu/drm/drm_dp_aux_dev.c
> +++ b/drivers/gpu/drm/drm_dp_aux_dev.c
> @@ -34,6 +34,7 @@
>  #include <linux/uaccess.h>
>  #include <linux/uio.h>
>  #include <drm/drm_dp_helper.h>
> +#include <drm/drm_dp_mst_helper.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drmP.h>
>  
> @@ -160,7 +161,11 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb,
> struct iov_iter *to)
>  			break;
>  		}
>  
> -		res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
> +		if (aux_dev->aux->is_remote)
> +			res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf,
> todo);
> +		else
> +			res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
> +

mhhhh, is there a reason we can't just use the normal ->read and ->write
auxdev callbacks for this instead of mixing layers like this?

>  		if (res <= 0)
>  			break;
>  
> @@ -207,7 +212,11 @@ static ssize_t auxdev_write_iter(struct kiocb *iocb,
> struct iov_iter *from)
>  			break;
>  		}
>  
> -		res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
> +		if (aux_dev->aux->is_remote)
> +			res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf,
> todo);
> +		else
> +			res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
> +


>  		if (res <= 0)
>  			break;
>  
> diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c
> b/drivers/gpu/drm/drm_dp_mst_topology.c
> index 2ab16c9..d5282db 100644
> --- a/drivers/gpu/drm/drm_dp_mst_topology.c
> +++ b/drivers/gpu/drm/drm_dp_mst_topology.c
> @@ -35,6 +35,8 @@
>  #include <drm/drm_atomic_helper.h>
>  #include <drm/drm_crtc_helper.h>
>  
> +#include "drm_crtc_helper_internal.h"
> +
>  /**
>   * DOC: dp mst helper
>   *
> @@ -52,6 +54,9 @@ static int drm_dp_dpcd_write_payload(struct
> drm_dp_mst_topology_mgr *mgr,
>  				     int id,
>  				     struct drm_dp_payload *payload);
>  
> +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);
> @@ -941,6 +946,8 @@ static void drm_dp_destroy_port(struct kref *kref)
>  	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
>  
>  	if (!port->input) {
> +		drm_dp_aux_unregister_devnode(&port->aux);
> +
>  		port->vcpi.num_slots = 0;
>  
>  		kfree(port->cached_edid);
> @@ -1095,6 +1102,46 @@ static bool drm_dp_port_setup_pdt(struct
> drm_dp_mst_port *port)
>  	return send_link;
>  }
>  
> +/**
> + * 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.
> + */
> +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.
> + */
> +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 void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8
> *guid)
>  {
>  	int ret;
> @@ -1158,6 +1205,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch
> *mstb,
>  		port->mgr = mstb->mgr;
>  		port->aux.name = "DPMST";
>  		port->aux.dev = dev->dev;
> +		port->aux.is_remote = true;
>  		created = true;
>  	} else {
>  		old_pdt = port->pdt;
> @@ -1188,7 +1236,7 @@ static void drm_dp_add_port(struct drm_dp_mst_branch
> *mstb,
>  				drm_dp_send_enum_path_resources(mstb->mgr,
> mstb, port);
>  		} else {
>  			port->available_pbn = 0;
> -			}
> +		}
>  	}
>  
>  	if (old_pdt != port->pdt && !port->input) {
> @@ -1220,6 +1268,14 @@ static void drm_dp_add_port(struct drm_dp_mst_branch
> *mstb,
>  			drm_connector_set_tile_property(port->connector);
>  		}
>  		(*mstb->mgr->cbs->register_connector)(port->connector);
> +
> +		/* Prepend an '_' in front to suffix the aux device filename
> */
> +		memmove(proppath + 1, proppath, sizeof(proppath) - 1);
> +		proppath[0] = '_';
> +
> +		/* Only create aux devices for physical ports with a TX or
> RX*/
> +		if (port->port_num < DP_MST_LOGICAL_PORT_0)
> +			drm_dp_aux_register_devnode(&port->aux, proppath);
>  	}
>  
>  out:
> @@ -1404,7 +1460,6 @@ static bool drm_dp_validate_guid(struct
> drm_dp_mst_topology_mgr *mgr,
>  	return false;
>  }
>  
> -#if 0
>  static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num,
> u32 offset, u8 num_bytes)
>  {
>  	struct drm_dp_sideband_msg_req_body req;
> @@ -1417,7 +1472,6 @@ static int build_dpcd_read(struct
> drm_dp_sideband_msg_tx *msg, u8 port_num, u32
>  
>  	return 0;
>  }
> -#endif
>  
>  static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
>  				    bool up, u8 *msg, int len)
> @@ -1994,26 +2048,55 @@ int drm_dp_update_payload_part2(struct
> drm_dp_mst_topology_mgr *mgr)
>  }
>  EXPORT_SYMBOL(drm_dp_update_payload_part2);
>  
> -#if 0 /* unused as of yet */
>  static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
>  				 struct drm_dp_mst_port *port,
> -				 int offset, int size)
> +				 int offset, int size, u8 *bytes)
>  {
>  	int len;
> +	int ret = 0;
>  	struct drm_dp_sideband_msg_tx *txmsg;
> +	struct drm_dp_mst_branch *mstb;
> +
> +	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
> +	if (!mstb)
> +		return -EINVAL;
>  
>  	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
> -	if (!txmsg)
> -		return -ENOMEM;
> +	if (!txmsg) {
> +		ret = -ENOMEM;
> +		goto fail_put;
> +	}
>  
> -	len = build_dpcd_read(txmsg, port->port_num, 0, 8);
> +	len = build_dpcd_read(txmsg, port->port_num, offset, size);
>  	txmsg->dst = port->parent;
>  
>  	drm_dp_queue_down_tx(mgr, txmsg);
>  
> -	return 0;
> +	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
> +	if (ret < 0)
> +		goto fail_free;
> +
> +	/* DPCD read should never be NACKed */
> +	if (WARN_ON_ONCE(txmsg->reply.reply_type == 1)) {
> +		ret = -EIO;
> +		goto fail_free;
> +	}
> +
> +	if (txmsg->reply.u.remote_dpcd_read_ack.num_bytes != size) {
> +		ret = -EPROTO;
> +		goto fail_free;
> +	}
> +
> +	ret = min_t(size_t, txmsg->reply.u.remote_dpcd_read_ack.num_bytes,
> size);
> +	memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, ret);
> +
> +fail_free:
> +	kfree(txmsg);
> +fail_put:
> +	drm_dp_put_mst_branch_device(mstb);
> +
> +	return ret;
>  }
> -#endif
>  
>  static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
>  				  struct drm_dp_mst_port *port,
> @@ -2041,9 +2124,9 @@ static int drm_dp_send_dpcd_write(struct
> drm_dp_mst_topology_mgr *mgr,
>  
>  	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
>  	if (ret > 0) {
> -		if (txmsg->reply.reply_type == 1) {
> -			ret = -EINVAL;
> -		} else
> +		if (txmsg->reply.reply_type == 1)
> +			ret = -EIO;
> +		else
>  			ret = 0;
>  	}
>  	kfree(txmsg);
> diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
> index 509667e..6dea76a 100644
> --- a/include/drm/drm_dp_helper.h
> +++ b/include/drm/drm_dp_helper.h
> @@ -1265,6 +1265,10 @@ struct drm_dp_aux {
>  	 * @cec: struct containing fields used for CEC-Tunneling-over-AUX.
>  	 */
>  	struct drm_dp_aux_cec cec;
> +	/**
> +	 * @is_remote: Is this "AUX CH" actually using sideband messaging.
> +	 */
> +	bool is_remote;
>  };
>  
>  ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
> diff --git a/include/drm/drm_dp_mst_helper.h
> b/include/drm/drm_dp_mst_helper.h
> index 371cc28..30f8c11 100644
> --- a/include/drm/drm_dp_mst_helper.h
> +++ b/include/drm/drm_dp_mst_helper.h
> @@ -615,6 +615,12 @@ void drm_dp_mst_dump_topology(struct seq_file *m,
>  
>  void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
>  int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
> +
> +ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
> +			     unsigned int offset, void *buffer, size_t size);
> +ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
> +			      unsigned int offset, void *buffer, size_t size);
> +
>  struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct
> drm_atomic_state *state,
>  								    struct
> drm_dp_mst_topology_mgr *mgr);
>  int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,
Ville Syrjälä April 17, 2019, 1:09 p.m. UTC | #2
On Tue, Apr 16, 2019 at 06:22:33PM -0400, Lyude Paul wrote:
> On Fri, 2019-04-12 at 12:05 -0400, sunpeng.li@amd.com wrote:
> > From: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > 
> > Expose AUX devices for MST ports, similar to how they are exposed for
> > SST.
> > 
> > The registered device will have it's MST port path appended in order to
> > identify it. i.e. /dev/drm_dp_aux4_mst:0-2-1
> > 
> > So for a MST topology like so:
> > 
> >                +---------+
> >                |  ASIC   |
> >                +---------+
> >               Conn-0|
> >                     |
> >                +----v----+
> >           +----| MST HUB |----+
> >           |    +---------+    |
> >           |                   |
> >           |Port-1       Port-2|
> >     +-----v-----+       +-----v-----+
> >     |  MST      |       |  SST      |
> >     |  Display  |       |  Display  |
> >     +-----------+       +-----------+
> >           |Port-1
> >           x
> > 
> > The list of AUX device names will look like:
> > 
> > AUX Device Name       | MST Device
> > ----------------------+----------------------------------
> > drm_dp_aux0           | MST Hub
> > drm_dp_aux1_mst:0-1-1 | MST Display's disconnected DP out
> > drm_dp_aux2_mst:0-1   | MST Display
> > drm_dp_aux3_mst:0-2   | SST Display
> > 
> > Note that aux devices are only created for Physical Ports. Logical Ports
> > are left out, since they are internally connected within the MST device
> > (not connected to a DP RX or TX).
> > 
> > Leo Li:
> > * Add missing drm_crtc_helper_internal.h include
> > * Fix hard-coded offset and size in drm_dp_send_dpcd_read()
> > * Only create aux devices for physical ports.
> > 
> > Signed-off-by: Ville Syrjälä <ville.syrjala@linux.intel.com>
> > Signed-off-by: Leo Li <sunpeng.li@amd.com>
> > ---
> >  drivers/gpu/drm/drm_dp_aux_dev.c      |  13 +++-
> >  drivers/gpu/drm/drm_dp_mst_topology.c | 109 ++++++++++++++++++++++++++++++-
> > ---
> >  include/drm/drm_dp_helper.h           |   4 ++
> >  include/drm/drm_dp_mst_helper.h       |   6 ++
> >  4 files changed, 117 insertions(+), 15 deletions(-)
> > 
> > diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c
> > b/drivers/gpu/drm/drm_dp_aux_dev.c
> > index 2310a67..f1241d1 100644
> > --- a/drivers/gpu/drm/drm_dp_aux_dev.c
> > +++ b/drivers/gpu/drm/drm_dp_aux_dev.c
> > @@ -34,6 +34,7 @@
> >  #include <linux/uaccess.h>
> >  #include <linux/uio.h>
> >  #include <drm/drm_dp_helper.h>
> > +#include <drm/drm_dp_mst_helper.h>
> >  #include <drm/drm_crtc.h>
> >  #include <drm/drmP.h>
> >  
> > @@ -160,7 +161,11 @@ static ssize_t auxdev_read_iter(struct kiocb *iocb,
> > struct iov_iter *to)
> >  			break;
> >  		}
> >  
> > -		res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
> > +		if (aux_dev->aux->is_remote)
> > +			res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf,
> > todo);
> > +		else
> > +			res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
> > +
> 
> mhhhh, is there a reason we can't just use the normal ->read and ->write
> auxdev callbacks for this instead of mixing layers like this?

IIRC I did think about that when I initially did this, but decided 
there was actual work involved so didn't bother with it, especially
since the code wasn't even working yet. Sadly I've forgotten what
the "acutal work" part was supposed to be.

Patch
diff mbox series

diff --git a/drivers/gpu/drm/drm_dp_aux_dev.c b/drivers/gpu/drm/drm_dp_aux_dev.c
index 2310a67..f1241d1 100644
--- a/drivers/gpu/drm/drm_dp_aux_dev.c
+++ b/drivers/gpu/drm/drm_dp_aux_dev.c
@@ -34,6 +34,7 @@ 
 #include <linux/uaccess.h>
 #include <linux/uio.h>
 #include <drm/drm_dp_helper.h>
+#include <drm/drm_dp_mst_helper.h>
 #include <drm/drm_crtc.h>
 #include <drm/drmP.h>
 
@@ -160,7 +161,11 @@  static ssize_t auxdev_read_iter(struct kiocb *iocb, struct iov_iter *to)
 			break;
 		}
 
-		res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
+		if (aux_dev->aux->is_remote)
+			res = drm_dp_mst_dpcd_read(aux_dev->aux, pos, buf, todo);
+		else
+			res = drm_dp_dpcd_read(aux_dev->aux, pos, buf, todo);
+
 		if (res <= 0)
 			break;
 
@@ -207,7 +212,11 @@  static ssize_t auxdev_write_iter(struct kiocb *iocb, struct iov_iter *from)
 			break;
 		}
 
-		res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
+		if (aux_dev->aux->is_remote)
+			res = drm_dp_mst_dpcd_write(aux_dev->aux, pos, buf, todo);
+		else
+			res = drm_dp_dpcd_write(aux_dev->aux, pos, buf, todo);
+
 		if (res <= 0)
 			break;
 
diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c
index 2ab16c9..d5282db 100644
--- a/drivers/gpu/drm/drm_dp_mst_topology.c
+++ b/drivers/gpu/drm/drm_dp_mst_topology.c
@@ -35,6 +35,8 @@ 
 #include <drm/drm_atomic_helper.h>
 #include <drm/drm_crtc_helper.h>
 
+#include "drm_crtc_helper_internal.h"
+
 /**
  * DOC: dp mst helper
  *
@@ -52,6 +54,9 @@  static int drm_dp_dpcd_write_payload(struct drm_dp_mst_topology_mgr *mgr,
 				     int id,
 				     struct drm_dp_payload *payload);
 
+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);
@@ -941,6 +946,8 @@  static void drm_dp_destroy_port(struct kref *kref)
 	struct drm_dp_mst_topology_mgr *mgr = port->mgr;
 
 	if (!port->input) {
+		drm_dp_aux_unregister_devnode(&port->aux);
+
 		port->vcpi.num_slots = 0;
 
 		kfree(port->cached_edid);
@@ -1095,6 +1102,46 @@  static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
 	return send_link;
 }
 
+/**
+ * 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.
+ */
+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.
+ */
+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 void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
 {
 	int ret;
@@ -1158,6 +1205,7 @@  static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 		port->mgr = mstb->mgr;
 		port->aux.name = "DPMST";
 		port->aux.dev = dev->dev;
+		port->aux.is_remote = true;
 		created = true;
 	} else {
 		old_pdt = port->pdt;
@@ -1188,7 +1236,7 @@  static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 				drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
 		} else {
 			port->available_pbn = 0;
-			}
+		}
 	}
 
 	if (old_pdt != port->pdt && !port->input) {
@@ -1220,6 +1268,14 @@  static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
 			drm_connector_set_tile_property(port->connector);
 		}
 		(*mstb->mgr->cbs->register_connector)(port->connector);
+
+		/* Prepend an '_' in front to suffix the aux device filename */
+		memmove(proppath + 1, proppath, sizeof(proppath) - 1);
+		proppath[0] = '_';
+
+		/* Only create aux devices for physical ports with a TX or RX*/
+		if (port->port_num < DP_MST_LOGICAL_PORT_0)
+			drm_dp_aux_register_devnode(&port->aux, proppath);
 	}
 
 out:
@@ -1404,7 +1460,6 @@  static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
 	return false;
 }
 
-#if 0
 static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32 offset, u8 num_bytes)
 {
 	struct drm_dp_sideband_msg_req_body req;
@@ -1417,7 +1472,6 @@  static int build_dpcd_read(struct drm_dp_sideband_msg_tx *msg, u8 port_num, u32
 
 	return 0;
 }
-#endif
 
 static int drm_dp_send_sideband_msg(struct drm_dp_mst_topology_mgr *mgr,
 				    bool up, u8 *msg, int len)
@@ -1994,26 +2048,55 @@  int drm_dp_update_payload_part2(struct drm_dp_mst_topology_mgr *mgr)
 }
 EXPORT_SYMBOL(drm_dp_update_payload_part2);
 
-#if 0 /* unused as of yet */
 static int drm_dp_send_dpcd_read(struct drm_dp_mst_topology_mgr *mgr,
 				 struct drm_dp_mst_port *port,
-				 int offset, int size)
+				 int offset, int size, u8 *bytes)
 {
 	int len;
+	int ret = 0;
 	struct drm_dp_sideband_msg_tx *txmsg;
+	struct drm_dp_mst_branch *mstb;
+
+	mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
+	if (!mstb)
+		return -EINVAL;
 
 	txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
-	if (!txmsg)
-		return -ENOMEM;
+	if (!txmsg) {
+		ret = -ENOMEM;
+		goto fail_put;
+	}
 
-	len = build_dpcd_read(txmsg, port->port_num, 0, 8);
+	len = build_dpcd_read(txmsg, port->port_num, offset, size);
 	txmsg->dst = port->parent;
 
 	drm_dp_queue_down_tx(mgr, txmsg);
 
-	return 0;
+	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+	if (ret < 0)
+		goto fail_free;
+
+	/* DPCD read should never be NACKed */
+	if (WARN_ON_ONCE(txmsg->reply.reply_type == 1)) {
+		ret = -EIO;
+		goto fail_free;
+	}
+
+	if (txmsg->reply.u.remote_dpcd_read_ack.num_bytes != size) {
+		ret = -EPROTO;
+		goto fail_free;
+	}
+
+	ret = min_t(size_t, txmsg->reply.u.remote_dpcd_read_ack.num_bytes, size);
+	memcpy(bytes, txmsg->reply.u.remote_dpcd_read_ack.bytes, ret);
+
+fail_free:
+	kfree(txmsg);
+fail_put:
+	drm_dp_put_mst_branch_device(mstb);
+
+	return ret;
 }
-#endif
 
 static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
 				  struct drm_dp_mst_port *port,
@@ -2041,9 +2124,9 @@  static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
 
 	ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
 	if (ret > 0) {
-		if (txmsg->reply.reply_type == 1) {
-			ret = -EINVAL;
-		} else
+		if (txmsg->reply.reply_type == 1)
+			ret = -EIO;
+		else
 			ret = 0;
 	}
 	kfree(txmsg);
diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
index 509667e..6dea76a 100644
--- a/include/drm/drm_dp_helper.h
+++ b/include/drm/drm_dp_helper.h
@@ -1265,6 +1265,10 @@  struct drm_dp_aux {
 	 * @cec: struct containing fields used for CEC-Tunneling-over-AUX.
 	 */
 	struct drm_dp_aux_cec cec;
+	/**
+	 * @is_remote: Is this "AUX CH" actually using sideband messaging.
+	 */
+	bool is_remote;
 };
 
 ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
diff --git a/include/drm/drm_dp_mst_helper.h b/include/drm/drm_dp_mst_helper.h
index 371cc28..30f8c11 100644
--- a/include/drm/drm_dp_mst_helper.h
+++ b/include/drm/drm_dp_mst_helper.h
@@ -615,6 +615,12 @@  void drm_dp_mst_dump_topology(struct seq_file *m,
 
 void drm_dp_mst_topology_mgr_suspend(struct drm_dp_mst_topology_mgr *mgr);
 int drm_dp_mst_topology_mgr_resume(struct drm_dp_mst_topology_mgr *mgr);
+
+ssize_t drm_dp_mst_dpcd_read(struct drm_dp_aux *aux,
+			     unsigned int offset, void *buffer, size_t size);
+ssize_t drm_dp_mst_dpcd_write(struct drm_dp_aux *aux,
+			      unsigned int offset, void *buffer, size_t size);
+
 struct drm_dp_mst_topology_state *drm_atomic_get_mst_topology_state(struct drm_atomic_state *state,
 								    struct drm_dp_mst_topology_mgr *mgr);
 int drm_dp_atomic_find_vcpi_slots(struct drm_atomic_state *state,