mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
thunderbolt: Add support for DisplayPort bandwidth allocation mode
The USB4 spec defines an optional feature that allows the connection manager to negotiate with the graphics through DPCD registers changes in the bandwidth allocation dynamically. This is referred as "bandwidth allocation mode" in the spec. The connection manager uses DP IN adapters registers to communicate with the graphics, and also gets notifications from these adapters when the graphics wants to change the bandwidth allocation. Both the connection manager and the graphics driver needs to support this. We check if the DP IN adapter supports this and if it does enable it before establishing a DP tunnel. Then we react on DP_BW notifications coming from the DP IN adapter and update the bandwidth allocation accordingly (within the maximum common capabilities the DP IN/OUT support). Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com>
This commit is contained in:
@@ -230,7 +230,6 @@ static int check_config_address(struct tb_cfg_address addr,
|
||||
static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
{
|
||||
struct cfg_error_pkg *pkg = response->buffer;
|
||||
struct tb_ctl *ctl = response->ctl;
|
||||
struct tb_cfg_result res = { 0 };
|
||||
res.response_route = tb_cfg_get_route(&pkg->header);
|
||||
res.response_port = 0;
|
||||
@@ -239,13 +238,6 @@ static struct tb_cfg_result decode_error(const struct ctl_pkg *response)
|
||||
if (res.err)
|
||||
return res;
|
||||
|
||||
if (pkg->zero1)
|
||||
tb_ctl_warn(ctl, "pkg->zero1 is %#x\n", pkg->zero1);
|
||||
if (pkg->zero2)
|
||||
tb_ctl_warn(ctl, "pkg->zero2 is %#x\n", pkg->zero2);
|
||||
if (pkg->zero3)
|
||||
tb_ctl_warn(ctl, "pkg->zero3 is %#x\n", pkg->zero3);
|
||||
|
||||
res.err = 1;
|
||||
res.tb_error = pkg->error;
|
||||
res.response_port = pkg->port;
|
||||
@@ -416,6 +408,7 @@ static int tb_async_error(const struct ctl_pkg *pkg)
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
return true;
|
||||
|
||||
default:
|
||||
@@ -735,6 +728,47 @@ void tb_ctl_stop(struct tb_ctl *ctl)
|
||||
|
||||
/* public interface, commands */
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_notification() - Ack notification
|
||||
* @ctl: Control channel to use
|
||||
* @route: Router that originated the event
|
||||
* @error: Pointer to the notification package
|
||||
*
|
||||
* Call this as response for non-plug notification to ack it. Returns
|
||||
* %0 on success or an error code on failure.
|
||||
*/
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error)
|
||||
{
|
||||
struct cfg_ack_pkg pkg = {
|
||||
.header = tb_cfg_make_header(route),
|
||||
};
|
||||
const char *name;
|
||||
|
||||
switch (error->error) {
|
||||
case TB_CFG_ERROR_LINK_ERROR:
|
||||
name = "link error";
|
||||
break;
|
||||
case TB_CFG_ERROR_HEC_ERROR_DETECTED:
|
||||
name = "HEC error";
|
||||
break;
|
||||
case TB_CFG_ERROR_FLOW_CONTROL_ERROR:
|
||||
name = "flow control error";
|
||||
break;
|
||||
case TB_CFG_ERROR_DP_BW:
|
||||
name = "DP_BW";
|
||||
break;
|
||||
default:
|
||||
name = "unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
tb_ctl_dbg(ctl, "acking %s (%#x) notification on %llx\n", name,
|
||||
error->error, route);
|
||||
|
||||
return tb_ctl_tx(ctl, &pkg, sizeof(pkg), TB_CFG_PKG_NOTIFY_ACK);
|
||||
}
|
||||
|
||||
/**
|
||||
* tb_cfg_ack_plug() - Ack hot plug/unplug event
|
||||
* @ctl: Control channel to use
|
||||
|
||||
@@ -122,6 +122,8 @@ static inline struct tb_cfg_header tb_cfg_make_header(u64 route)
|
||||
return header;
|
||||
}
|
||||
|
||||
int tb_cfg_ack_notification(struct tb_ctl *ctl, u64 route,
|
||||
const struct cfg_error_pkg *error);
|
||||
int tb_cfg_ack_plug(struct tb_ctl *ctl, u64 route, u32 port, bool unplug);
|
||||
struct tb_cfg_result tb_cfg_reset(struct tb_ctl *ctl, u64 route);
|
||||
struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer,
|
||||
|
||||
+479
-14
File diff suppressed because it is too large
Load Diff
@@ -223,6 +223,23 @@ struct tb_switch {
|
||||
enum tb_clx clx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_bandwidth_group - Bandwidth management group
|
||||
* @tb: Pointer to the domain the group belongs to
|
||||
* @index: Index of the group (aka Group_ID). Valid values %1-%7
|
||||
* @ports: DP IN adapters belonging to this group are linked here
|
||||
*
|
||||
* Any tunnel that requires isochronous bandwidth (that's DP for now) is
|
||||
* attached to a bandwidth group. All tunnels going through the same
|
||||
* USB4 links share the same group and can dynamically distribute the
|
||||
* bandwidth within the group.
|
||||
*/
|
||||
struct tb_bandwidth_group {
|
||||
struct tb *tb;
|
||||
int index;
|
||||
struct list_head ports;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct tb_port - a thunderbolt port, part of a tb_switch
|
||||
* @config: Cached port configuration read from registers
|
||||
@@ -247,6 +264,9 @@ struct tb_switch {
|
||||
* @ctl_credits: Buffers reserved for control path
|
||||
* @dma_credits: Number of credits allocated for DMA tunneling for all
|
||||
* DMA paths through this port.
|
||||
* @group: Bandwidth allocation group the adapter is assigned to. Only
|
||||
* used for DP IN adapters for now.
|
||||
* @group_list: The adapter is linked to the group's list of ports through this
|
||||
*
|
||||
* In USB4 terminology this structure represents an adapter (protocol or
|
||||
* lane adapter).
|
||||
@@ -272,6 +292,8 @@ struct tb_port {
|
||||
unsigned int total_credits;
|
||||
unsigned int ctl_credits;
|
||||
unsigned int dma_credits;
|
||||
struct tb_bandwidth_group *group;
|
||||
struct list_head group_list;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,7 @@ enum tb_cfg_error {
|
||||
TB_CFG_ERROR_HEC_ERROR_DETECTED = 12,
|
||||
TB_CFG_ERROR_FLOW_CONTROL_ERROR = 13,
|
||||
TB_CFG_ERROR_LOCK = 15,
|
||||
TB_CFG_ERROR_DP_BW = 32,
|
||||
};
|
||||
|
||||
/* common header */
|
||||
@@ -64,14 +65,16 @@ struct cfg_write_pkg {
|
||||
/* TB_CFG_PKG_ERROR */
|
||||
struct cfg_error_pkg {
|
||||
struct tb_cfg_header header;
|
||||
enum tb_cfg_error error:4;
|
||||
u32 zero1:4;
|
||||
enum tb_cfg_error error:8;
|
||||
u32 port:6;
|
||||
u32 zero2:2; /* Both should be zero, still they are different fields. */
|
||||
u32 zero3:14;
|
||||
u32 reserved:16;
|
||||
u32 pg:2;
|
||||
} __packed;
|
||||
|
||||
struct cfg_ack_pkg {
|
||||
struct tb_cfg_header header;
|
||||
};
|
||||
|
||||
#define TB_CFG_ERROR_PG_HOT_PLUG 0x2
|
||||
#define TB_CFG_ERROR_PG_HOT_UNPLUG 0x3
|
||||
|
||||
|
||||
+453
-30
File diff suppressed because it is too large
Load Diff
@@ -29,6 +29,9 @@ enum tb_tunnel_type {
|
||||
* @init: Optional tunnel specific initialization
|
||||
* @deinit: Optional tunnel specific de-initialization
|
||||
* @activate: Optional tunnel specific activation/deactivation
|
||||
* @maximum_bandwidth:
|
||||
* @allocated_bandwidth: Return how much bandwidth is allocated for the tunnel
|
||||
* @alloc_bandwidth: Change tunnel bandwidth allocation
|
||||
* @consumed_bandwidth: Return how much bandwidth the tunnel consumes
|
||||
* @release_unused_bandwidth: Release all unused bandwidth
|
||||
* @reclaim_available_bandwidth: Reclaim back available bandwidth
|
||||
@@ -40,6 +43,8 @@ enum tb_tunnel_type {
|
||||
* Only set if the bandwidth needs to be limited.
|
||||
* @allocated_up: Allocated upstream bandwidth (only for USB3)
|
||||
* @allocated_down: Allocated downstream bandwidth (only for USB3)
|
||||
* @bw_mode: DP bandwidth allocation mode registers can be used to
|
||||
* determine consumed and allocated bandwidth
|
||||
*/
|
||||
struct tb_tunnel {
|
||||
struct tb *tb;
|
||||
@@ -50,6 +55,12 @@ struct tb_tunnel {
|
||||
int (*init)(struct tb_tunnel *tunnel);
|
||||
void (*deinit)(struct tb_tunnel *tunnel);
|
||||
int (*activate)(struct tb_tunnel *tunnel, bool activate);
|
||||
int (*maximum_bandwidth)(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int (*allocated_bandwidth)(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int (*alloc_bandwidth)(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int (*consumed_bandwidth)(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int (*release_unused_bandwidth)(struct tb_tunnel *tunnel);
|
||||
@@ -62,6 +73,7 @@ struct tb_tunnel {
|
||||
int max_down;
|
||||
int allocated_up;
|
||||
int allocated_down;
|
||||
bool bw_mode;
|
||||
};
|
||||
|
||||
struct tb_tunnel *tb_tunnel_discover_pci(struct tb *tb, struct tb_port *down,
|
||||
@@ -92,6 +104,12 @@ void tb_tunnel_deactivate(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_is_invalid(struct tb_tunnel *tunnel);
|
||||
bool tb_tunnel_port_on_path(const struct tb_tunnel *tunnel,
|
||||
const struct tb_port *port);
|
||||
int tb_tunnel_maximum_bandwidth(struct tb_tunnel *tunnel, int *max_up,
|
||||
int *max_down);
|
||||
int tb_tunnel_allocated_bandwidth(struct tb_tunnel *tunnel, int *allocated_up,
|
||||
int *allocated_down);
|
||||
int tb_tunnel_alloc_bandwidth(struct tb_tunnel *tunnel, int *alloc_up,
|
||||
int *alloc_down);
|
||||
int tb_tunnel_consumed_bandwidth(struct tb_tunnel *tunnel, int *consumed_up,
|
||||
int *consumed_down);
|
||||
int tb_tunnel_release_unused_bandwidth(struct tb_tunnel *tunnel);
|
||||
|
||||
Reference in New Issue
Block a user