mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-10-28 07:13:34 +00:00
Merge branch 'drm-fixes-mst' of git://people.freedesktop.org/~airlied/linux into drm-fixes
displayport multistream fixes from AMD. * 'drm-fixes-mst' of git://people.freedesktop.org/~airlied/linux: drm/dp/mst: deallocate payload on port destruction drm/dp/mst: Reverse order of MST enable and clearing VC payload table. drm/dp/mst: move GUID storage from mgr, port to only mst branch drm/dp/mst: change MST detection scheme drm/dp/mst: Calculate MST PBN with 31.32 fixed point drm: Add drm_fixp_from_fraction and drm_fixp2int_ceil drm/mst: Add range check for max_payloads during init drm/mst: Don't ignore the MST PBN self-test result drm: fix missing reference counting decrease
This commit is contained in:
commit
6739b3d7bc
3 changed files with 239 additions and 110 deletions
|
@ -803,12 +803,33 @@ static struct drm_dp_mst_branch *drm_dp_add_mst_branch_device(u8 lct, u8 *rad)
|
||||||
return mstb;
|
return mstb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void drm_dp_free_mst_port(struct kref *kref);
|
||||||
|
|
||||||
|
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, kref);
|
||||||
|
if (mstb->port_parent) {
|
||||||
|
if (list_empty(&mstb->port_parent->next))
|
||||||
|
kref_put(&mstb->port_parent->kref, drm_dp_free_mst_port);
|
||||||
|
}
|
||||||
|
kfree(mstb);
|
||||||
|
}
|
||||||
|
|
||||||
static void drm_dp_destroy_mst_branch_device(struct kref *kref)
|
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, kref);
|
struct drm_dp_mst_branch *mstb = container_of(kref, struct drm_dp_mst_branch, kref);
|
||||||
struct drm_dp_mst_port *port, *tmp;
|
struct drm_dp_mst_port *port, *tmp;
|
||||||
bool wake_tx = false;
|
bool wake_tx = false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* init kref again to be used by ports to remove mst branch when it is
|
||||||
|
* not needed anymore
|
||||||
|
*/
|
||||||
|
kref_init(kref);
|
||||||
|
|
||||||
|
if (mstb->port_parent && list_empty(&mstb->port_parent->next))
|
||||||
|
kref_get(&mstb->port_parent->kref);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* destroy all ports - don't need lock
|
* destroy all ports - don't need lock
|
||||||
* as there are no more references to the mst branch
|
* as there are no more references to the mst branch
|
||||||
|
@ -835,7 +856,8 @@ static void drm_dp_destroy_mst_branch_device(struct kref *kref)
|
||||||
|
|
||||||
if (wake_tx)
|
if (wake_tx)
|
||||||
wake_up(&mstb->mgr->tx_waitq);
|
wake_up(&mstb->mgr->tx_waitq);
|
||||||
kfree(mstb);
|
|
||||||
|
kref_put(kref, drm_dp_free_mst_branch_device);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
|
static void drm_dp_put_mst_branch_device(struct drm_dp_mst_branch *mstb)
|
||||||
|
@ -883,6 +905,7 @@ static void drm_dp_destroy_port(struct kref *kref)
|
||||||
* from an EDID retrieval */
|
* from an EDID retrieval */
|
||||||
|
|
||||||
mutex_lock(&mgr->destroy_connector_lock);
|
mutex_lock(&mgr->destroy_connector_lock);
|
||||||
|
kref_get(&port->parent->kref);
|
||||||
list_add(&port->next, &mgr->destroy_connector_list);
|
list_add(&port->next, &mgr->destroy_connector_list);
|
||||||
mutex_unlock(&mgr->destroy_connector_lock);
|
mutex_unlock(&mgr->destroy_connector_lock);
|
||||||
schedule_work(&mgr->destroy_connector_work);
|
schedule_work(&mgr->destroy_connector_work);
|
||||||
|
@ -1018,18 +1041,27 @@ static bool drm_dp_port_setup_pdt(struct drm_dp_mst_port *port)
|
||||||
return send_link;
|
return send_link;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void drm_dp_check_port_guid(struct drm_dp_mst_branch *mstb,
|
static void drm_dp_check_mstb_guid(struct drm_dp_mst_branch *mstb, u8 *guid)
|
||||||
struct drm_dp_mst_port *port)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
if (port->dpcd_rev >= 0x12) {
|
|
||||||
port->guid_valid = drm_dp_validate_guid(mstb->mgr, port->guid);
|
memcpy(mstb->guid, guid, 16);
|
||||||
if (!port->guid_valid) {
|
|
||||||
ret = drm_dp_send_dpcd_write(mstb->mgr,
|
if (!drm_dp_validate_guid(mstb->mgr, mstb->guid)) {
|
||||||
port,
|
if (mstb->port_parent) {
|
||||||
DP_GUID,
|
ret = drm_dp_send_dpcd_write(
|
||||||
16, port->guid);
|
mstb->mgr,
|
||||||
port->guid_valid = true;
|
mstb->port_parent,
|
||||||
|
DP_GUID,
|
||||||
|
16,
|
||||||
|
mstb->guid);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
ret = drm_dp_dpcd_write(
|
||||||
|
mstb->mgr->aux,
|
||||||
|
DP_GUID,
|
||||||
|
mstb->guid,
|
||||||
|
16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1086,7 +1118,6 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
|
||||||
port->dpcd_rev = port_msg->dpcd_revision;
|
port->dpcd_rev = port_msg->dpcd_revision;
|
||||||
port->num_sdp_streams = port_msg->num_sdp_streams;
|
port->num_sdp_streams = port_msg->num_sdp_streams;
|
||||||
port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
|
port->num_sdp_stream_sinks = port_msg->num_sdp_stream_sinks;
|
||||||
memcpy(port->guid, port_msg->peer_guid, 16);
|
|
||||||
|
|
||||||
/* manage mstb port lists with mgr lock - take a reference
|
/* manage mstb port lists with mgr lock - take a reference
|
||||||
for this list */
|
for this list */
|
||||||
|
@ -1099,11 +1130,9 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
|
||||||
|
|
||||||
if (old_ddps != port->ddps) {
|
if (old_ddps != port->ddps) {
|
||||||
if (port->ddps) {
|
if (port->ddps) {
|
||||||
drm_dp_check_port_guid(mstb, port);
|
|
||||||
if (!port->input)
|
if (!port->input)
|
||||||
drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
|
drm_dp_send_enum_path_resources(mstb->mgr, mstb, port);
|
||||||
} else {
|
} else {
|
||||||
port->guid_valid = false;
|
|
||||||
port->available_pbn = 0;
|
port->available_pbn = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1130,13 +1159,11 @@ static void drm_dp_add_port(struct drm_dp_mst_branch *mstb,
|
||||||
drm_dp_put_port(port);
|
drm_dp_put_port(port);
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
if (port->port_num >= DP_MST_LOGICAL_PORT_0) {
|
|
||||||
port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc);
|
drm_mode_connector_set_tile_property(port->connector);
|
||||||
drm_mode_connector_set_tile_property(port->connector);
|
|
||||||
}
|
|
||||||
(*mstb->mgr->cbs->register_connector)(port->connector);
|
(*mstb->mgr->cbs->register_connector)(port->connector);
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
out:
|
||||||
/* put reference to this port */
|
/* put reference to this port */
|
||||||
drm_dp_put_port(port);
|
drm_dp_put_port(port);
|
||||||
|
@ -1161,11 +1188,9 @@ static void drm_dp_update_port(struct drm_dp_mst_branch *mstb,
|
||||||
port->ddps = conn_stat->displayport_device_plug_status;
|
port->ddps = conn_stat->displayport_device_plug_status;
|
||||||
|
|
||||||
if (old_ddps != port->ddps) {
|
if (old_ddps != port->ddps) {
|
||||||
|
dowork = true;
|
||||||
if (port->ddps) {
|
if (port->ddps) {
|
||||||
drm_dp_check_port_guid(mstb, port);
|
|
||||||
dowork = true;
|
|
||||||
} else {
|
} else {
|
||||||
port->guid_valid = false;
|
|
||||||
port->available_pbn = 0;
|
port->available_pbn = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1222,13 +1247,14 @@ static struct drm_dp_mst_branch *get_mst_branch_device_by_guid_helper(
|
||||||
struct drm_dp_mst_branch *found_mstb;
|
struct drm_dp_mst_branch *found_mstb;
|
||||||
struct drm_dp_mst_port *port;
|
struct drm_dp_mst_port *port;
|
||||||
|
|
||||||
|
if (memcmp(mstb->guid, guid, 16) == 0)
|
||||||
|
return mstb;
|
||||||
|
|
||||||
|
|
||||||
list_for_each_entry(port, &mstb->ports, next) {
|
list_for_each_entry(port, &mstb->ports, next) {
|
||||||
if (!port->mstb)
|
if (!port->mstb)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (port->guid_valid && memcmp(port->guid, guid, 16) == 0)
|
|
||||||
return port->mstb;
|
|
||||||
|
|
||||||
found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
|
found_mstb = get_mst_branch_device_by_guid_helper(port->mstb, guid);
|
||||||
|
|
||||||
if (found_mstb)
|
if (found_mstb)
|
||||||
|
@ -1247,10 +1273,7 @@ static struct drm_dp_mst_branch *drm_dp_get_mst_branch_device_by_guid(
|
||||||
/* find the port by iterating down */
|
/* find the port by iterating down */
|
||||||
mutex_lock(&mgr->lock);
|
mutex_lock(&mgr->lock);
|
||||||
|
|
||||||
if (mgr->guid_valid && memcmp(mgr->guid, guid, 16) == 0)
|
mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
|
||||||
mstb = mgr->mst_primary;
|
|
||||||
else
|
|
||||||
mstb = get_mst_branch_device_by_guid_helper(mgr->mst_primary, guid);
|
|
||||||
|
|
||||||
if (mstb)
|
if (mstb)
|
||||||
kref_get(&mstb->kref);
|
kref_get(&mstb->kref);
|
||||||
|
@ -1271,8 +1294,13 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m
|
||||||
if (port->input)
|
if (port->input)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!port->ddps)
|
if (!port->ddps) {
|
||||||
|
if (port->cached_edid) {
|
||||||
|
kfree(port->cached_edid);
|
||||||
|
port->cached_edid = NULL;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (!port->available_pbn)
|
if (!port->available_pbn)
|
||||||
drm_dp_send_enum_path_resources(mgr, mstb, port);
|
drm_dp_send_enum_path_resources(mgr, mstb, port);
|
||||||
|
@ -1283,6 +1311,12 @@ static void drm_dp_check_and_send_link_address(struct drm_dp_mst_topology_mgr *m
|
||||||
drm_dp_check_and_send_link_address(mgr, mstb_child);
|
drm_dp_check_and_send_link_address(mgr, mstb_child);
|
||||||
drm_dp_put_mst_branch_device(mstb_child);
|
drm_dp_put_mst_branch_device(mstb_child);
|
||||||
}
|
}
|
||||||
|
} else if (port->pdt == DP_PEER_DEVICE_SST_SINK ||
|
||||||
|
port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV) {
|
||||||
|
if (!port->cached_edid) {
|
||||||
|
port->cached_edid =
|
||||||
|
drm_get_edid(port->connector, &port->aux.ddc);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1302,6 +1336,8 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work)
|
||||||
drm_dp_check_and_send_link_address(mgr, mstb);
|
drm_dp_check_and_send_link_address(mgr, mstb);
|
||||||
drm_dp_put_mst_branch_device(mstb);
|
drm_dp_put_mst_branch_device(mstb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(*mgr->cbs->hotplug)(mgr);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
|
static bool drm_dp_validate_guid(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
|
@ -1555,10 +1591,12 @@ static void drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
|
txmsg->reply.u.link_addr.ports[i].num_sdp_streams,
|
||||||
txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
|
txmsg->reply.u.link_addr.ports[i].num_sdp_stream_sinks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drm_dp_check_mstb_guid(mstb, txmsg->reply.u.link_addr.guid);
|
||||||
|
|
||||||
for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
|
for (i = 0; i < txmsg->reply.u.link_addr.nports; i++) {
|
||||||
drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
|
drm_dp_add_port(mstb, mgr->dev, &txmsg->reply.u.link_addr.ports[i]);
|
||||||
}
|
}
|
||||||
(*mgr->cbs->hotplug)(mgr);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
mstb->link_address_sent = false;
|
mstb->link_address_sent = false;
|
||||||
|
@ -1602,6 +1640,37 @@ static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static struct drm_dp_mst_port *drm_dp_get_last_connected_port_to_mstb(struct drm_dp_mst_branch *mstb)
|
||||||
|
{
|
||||||
|
if (!mstb->port_parent)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (mstb->port_parent->mstb != mstb)
|
||||||
|
return mstb->port_parent;
|
||||||
|
|
||||||
|
return drm_dp_get_last_connected_port_to_mstb(mstb->port_parent->parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct drm_dp_mst_branch *drm_dp_get_last_connected_port_and_mstb(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
|
struct drm_dp_mst_branch *mstb,
|
||||||
|
int *port_num)
|
||||||
|
{
|
||||||
|
struct drm_dp_mst_branch *rmstb = NULL;
|
||||||
|
struct drm_dp_mst_port *found_port;
|
||||||
|
mutex_lock(&mgr->lock);
|
||||||
|
if (mgr->mst_primary) {
|
||||||
|
found_port = drm_dp_get_last_connected_port_to_mstb(mstb);
|
||||||
|
|
||||||
|
if (found_port) {
|
||||||
|
rmstb = found_port->parent;
|
||||||
|
kref_get(&rmstb->kref);
|
||||||
|
*port_num = found_port->port_num;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mutex_unlock(&mgr->lock);
|
||||||
|
return rmstb;
|
||||||
|
}
|
||||||
|
|
||||||
static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
|
static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
struct drm_dp_mst_port *port,
|
struct drm_dp_mst_port *port,
|
||||||
int id,
|
int id,
|
||||||
|
@ -1609,13 +1678,18 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
{
|
{
|
||||||
struct drm_dp_sideband_msg_tx *txmsg;
|
struct drm_dp_sideband_msg_tx *txmsg;
|
||||||
struct drm_dp_mst_branch *mstb;
|
struct drm_dp_mst_branch *mstb;
|
||||||
int len, ret;
|
int len, ret, port_num;
|
||||||
u8 sinks[DRM_DP_MAX_SDP_STREAMS];
|
u8 sinks[DRM_DP_MAX_SDP_STREAMS];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
|
port_num = port->port_num;
|
||||||
mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
|
mstb = drm_dp_get_validated_mstb_ref(mgr, port->parent);
|
||||||
if (!mstb)
|
if (!mstb) {
|
||||||
return -EINVAL;
|
mstb = drm_dp_get_last_connected_port_and_mstb(mgr, port->parent, &port_num);
|
||||||
|
|
||||||
|
if (!mstb)
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
|
txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
|
||||||
if (!txmsg) {
|
if (!txmsg) {
|
||||||
|
@ -1627,7 +1701,7 @@ static int drm_dp_payload_send_msg(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
sinks[i] = i;
|
sinks[i] = i;
|
||||||
|
|
||||||
txmsg->dst = mstb;
|
txmsg->dst = mstb;
|
||||||
len = build_allocate_payload(txmsg, port->port_num,
|
len = build_allocate_payload(txmsg, port_num,
|
||||||
id,
|
id,
|
||||||
pbn, port->num_sdp_streams, sinks);
|
pbn, port->num_sdp_streams, sinks);
|
||||||
|
|
||||||
|
@ -1983,6 +2057,12 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
|
||||||
mgr->mst_primary = mstb;
|
mgr->mst_primary = mstb;
|
||||||
kref_get(&mgr->mst_primary->kref);
|
kref_get(&mgr->mst_primary->kref);
|
||||||
|
|
||||||
|
ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
|
||||||
|
DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
|
||||||
|
if (ret < 0) {
|
||||||
|
goto out_unlock;
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
struct drm_dp_payload reset_pay;
|
struct drm_dp_payload reset_pay;
|
||||||
reset_pay.start_slot = 0;
|
reset_pay.start_slot = 0;
|
||||||
|
@ -1990,26 +2070,6 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
|
||||||
drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
|
drm_dp_dpcd_write_payload(mgr, 0, &reset_pay);
|
||||||
}
|
}
|
||||||
|
|
||||||
ret = drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL,
|
|
||||||
DP_MST_EN | DP_UP_REQ_EN | DP_UPSTREAM_IS_SRC);
|
|
||||||
if (ret < 0) {
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* sort out guid */
|
|
||||||
ret = drm_dp_dpcd_read(mgr->aux, DP_GUID, mgr->guid, 16);
|
|
||||||
if (ret != 16) {
|
|
||||||
DRM_DEBUG_KMS("failed to read DP GUID %d\n", ret);
|
|
||||||
goto out_unlock;
|
|
||||||
}
|
|
||||||
|
|
||||||
mgr->guid_valid = drm_dp_validate_guid(mgr, mgr->guid);
|
|
||||||
if (!mgr->guid_valid) {
|
|
||||||
ret = drm_dp_dpcd_write(mgr->aux, DP_GUID, mgr->guid, 16);
|
|
||||||
mgr->guid_valid = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
queue_work(system_long_wq, &mgr->work);
|
queue_work(system_long_wq, &mgr->work);
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
|
@ -2231,9 +2291,8 @@ static int drm_dp_mst_handle_up_req(struct drm_dp_mst_topology_mgr *mgr)
|
||||||
}
|
}
|
||||||
|
|
||||||
drm_dp_update_port(mstb, &msg.u.conn_stat);
|
drm_dp_update_port(mstb, &msg.u.conn_stat);
|
||||||
DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
|
|
||||||
(*mgr->cbs->hotplug)(mgr);
|
|
||||||
|
|
||||||
|
DRM_DEBUG_KMS("Got CSN: pn: %d ldps:%d ddps: %d mcs: %d ip: %d pdt: %d\n", msg.u.conn_stat.port_number, msg.u.conn_stat.legacy_device_plug_status, msg.u.conn_stat.displayport_device_plug_status, msg.u.conn_stat.message_capability_status, msg.u.conn_stat.input_port, msg.u.conn_stat.peer_device_type);
|
||||||
} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
|
} else if (msg.req_type == DP_RESOURCE_STATUS_NOTIFY) {
|
||||||
drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
|
drm_dp_send_up_ack_reply(mgr, mgr->mst_primary, msg.req_type, seqno, false);
|
||||||
if (!mstb)
|
if (!mstb)
|
||||||
|
@ -2320,10 +2379,6 @@ enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector
|
||||||
|
|
||||||
case DP_PEER_DEVICE_SST_SINK:
|
case DP_PEER_DEVICE_SST_SINK:
|
||||||
status = connector_status_connected;
|
status = connector_status_connected;
|
||||||
/* for logical ports - cache the EDID */
|
|
||||||
if (port->port_num >= 8 && !port->cached_edid) {
|
|
||||||
port->cached_edid = drm_get_edid(connector, &port->aux.ddc);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case DP_PEER_DEVICE_DP_LEGACY_CONV:
|
case DP_PEER_DEVICE_DP_LEGACY_CONV:
|
||||||
if (port->ldps)
|
if (port->ldps)
|
||||||
|
@ -2378,10 +2433,7 @@ struct edid *drm_dp_mst_get_edid(struct drm_connector *connector, struct drm_dp_
|
||||||
|
|
||||||
if (port->cached_edid)
|
if (port->cached_edid)
|
||||||
edid = drm_edid_duplicate(port->cached_edid);
|
edid = drm_edid_duplicate(port->cached_edid);
|
||||||
else {
|
|
||||||
edid = drm_get_edid(connector, &port->aux.ddc);
|
|
||||||
drm_mode_connector_set_tile_property(connector);
|
|
||||||
}
|
|
||||||
port->has_audio = drm_detect_monitor_audio(edid);
|
port->has_audio = drm_detect_monitor_audio(edid);
|
||||||
drm_dp_put_port(port);
|
drm_dp_put_port(port);
|
||||||
return edid;
|
return edid;
|
||||||
|
@ -2446,6 +2498,7 @@ bool drm_dp_mst_allocate_vcpi(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp
|
||||||
DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
|
DRM_DEBUG_KMS("payload: vcpi %d already allocated for pbn %d - requested pbn %d\n", port->vcpi.vcpi, port->vcpi.pbn, pbn);
|
||||||
if (pbn == port->vcpi.pbn) {
|
if (pbn == port->vcpi.pbn) {
|
||||||
*slots = port->vcpi.num_slots;
|
*slots = port->vcpi.num_slots;
|
||||||
|
drm_dp_put_port(port);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2605,32 +2658,31 @@ EXPORT_SYMBOL(drm_dp_check_act_status);
|
||||||
*/
|
*/
|
||||||
int drm_dp_calc_pbn_mode(int clock, int bpp)
|
int drm_dp_calc_pbn_mode(int clock, int bpp)
|
||||||
{
|
{
|
||||||
fixed20_12 pix_bw;
|
u64 kbps;
|
||||||
fixed20_12 fbpp;
|
s64 peak_kbps;
|
||||||
fixed20_12 result;
|
u32 numerator;
|
||||||
fixed20_12 margin, tmp;
|
u32 denominator;
|
||||||
u32 res;
|
|
||||||
|
|
||||||
pix_bw.full = dfixed_const(clock);
|
kbps = clock * bpp;
|
||||||
fbpp.full = dfixed_const(bpp);
|
|
||||||
tmp.full = dfixed_const(8);
|
|
||||||
fbpp.full = dfixed_div(fbpp, tmp);
|
|
||||||
|
|
||||||
result.full = dfixed_mul(pix_bw, fbpp);
|
/*
|
||||||
margin.full = dfixed_const(54);
|
* margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
|
||||||
tmp.full = dfixed_const(64);
|
* The unit of 54/64Mbytes/sec is an arbitrary unit chosen based on
|
||||||
margin.full = dfixed_div(margin, tmp);
|
* common multiplier to render an integer PBN for all link rate/lane
|
||||||
result.full = dfixed_div(result, margin);
|
* counts combinations
|
||||||
|
* calculate
|
||||||
|
* peak_kbps *= (1006/1000)
|
||||||
|
* peak_kbps *= (64/54)
|
||||||
|
* peak_kbps *= 8 convert to bytes
|
||||||
|
*/
|
||||||
|
|
||||||
margin.full = dfixed_const(1006);
|
numerator = 64 * 1006;
|
||||||
tmp.full = dfixed_const(1000);
|
denominator = 54 * 8 * 1000 * 1000;
|
||||||
margin.full = dfixed_div(margin, tmp);
|
|
||||||
result.full = dfixed_mul(result, margin);
|
|
||||||
|
|
||||||
result.full = dfixed_div(result, tmp);
|
kbps *= numerator;
|
||||||
result.full = dfixed_ceil(result);
|
peak_kbps = drm_fixp_from_fraction(kbps, denominator);
|
||||||
res = dfixed_trunc(result);
|
|
||||||
return res;
|
return drm_fixp2int_ceil(peak_kbps);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
|
EXPORT_SYMBOL(drm_dp_calc_pbn_mode);
|
||||||
|
|
||||||
|
@ -2638,11 +2690,23 @@ static int test_calc_pbn_mode(void)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
ret = drm_dp_calc_pbn_mode(154000, 30);
|
ret = drm_dp_calc_pbn_mode(154000, 30);
|
||||||
if (ret != 689)
|
if (ret != 689) {
|
||||||
|
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
|
||||||
|
154000, 30, 689, ret);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
ret = drm_dp_calc_pbn_mode(234000, 30);
|
ret = drm_dp_calc_pbn_mode(234000, 30);
|
||||||
if (ret != 1047)
|
if (ret != 1047) {
|
||||||
|
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
|
||||||
|
234000, 30, 1047, ret);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
ret = drm_dp_calc_pbn_mode(297000, 24);
|
||||||
|
if (ret != 1063) {
|
||||||
|
DRM_ERROR("PBN calculation test failed - clock %d, bpp %d, expected PBN %d, actual PBN %d.\n",
|
||||||
|
297000, 24, 1063, ret);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2783,6 +2847,13 @@ static void drm_dp_tx_work(struct work_struct *work)
|
||||||
mutex_unlock(&mgr->qlock);
|
mutex_unlock(&mgr->qlock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void drm_dp_free_mst_port(struct kref *kref)
|
||||||
|
{
|
||||||
|
struct drm_dp_mst_port *port = container_of(kref, struct drm_dp_mst_port, kref);
|
||||||
|
kref_put(&port->parent->kref, drm_dp_free_mst_branch_device);
|
||||||
|
kfree(port);
|
||||||
|
}
|
||||||
|
|
||||||
static void drm_dp_destroy_connector_work(struct work_struct *work)
|
static void drm_dp_destroy_connector_work(struct work_struct *work)
|
||||||
{
|
{
|
||||||
struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
|
struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work);
|
||||||
|
@ -2803,13 +2874,22 @@ static void drm_dp_destroy_connector_work(struct work_struct *work)
|
||||||
list_del(&port->next);
|
list_del(&port->next);
|
||||||
mutex_unlock(&mgr->destroy_connector_lock);
|
mutex_unlock(&mgr->destroy_connector_lock);
|
||||||
|
|
||||||
|
kref_init(&port->kref);
|
||||||
|
INIT_LIST_HEAD(&port->next);
|
||||||
|
|
||||||
mgr->cbs->destroy_connector(mgr, port->connector);
|
mgr->cbs->destroy_connector(mgr, port->connector);
|
||||||
|
|
||||||
drm_dp_port_teardown_pdt(port, port->pdt);
|
drm_dp_port_teardown_pdt(port, port->pdt);
|
||||||
|
|
||||||
if (!port->input && port->vcpi.vcpi > 0)
|
if (!port->input && port->vcpi.vcpi > 0) {
|
||||||
drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
|
if (mgr->mst_state) {
|
||||||
kfree(port);
|
drm_dp_mst_reset_vcpi_slots(mgr, port);
|
||||||
|
drm_dp_update_payload_part1(mgr);
|
||||||
|
drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kref_put(&port->kref, drm_dp_free_mst_port);
|
||||||
send_hotplug = true;
|
send_hotplug = true;
|
||||||
}
|
}
|
||||||
if (send_hotplug)
|
if (send_hotplug)
|
||||||
|
@ -2847,6 +2927,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
|
mgr->max_dpcd_transaction_bytes = max_dpcd_transaction_bytes;
|
||||||
mgr->max_payloads = max_payloads;
|
mgr->max_payloads = max_payloads;
|
||||||
mgr->conn_base_id = conn_base_id;
|
mgr->conn_base_id = conn_base_id;
|
||||||
|
if (max_payloads + 1 > sizeof(mgr->payload_mask) * 8 ||
|
||||||
|
max_payloads + 1 > sizeof(mgr->vcpi_mask) * 8)
|
||||||
|
return -EINVAL;
|
||||||
mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
|
mgr->payloads = kcalloc(max_payloads, sizeof(struct drm_dp_payload), GFP_KERNEL);
|
||||||
if (!mgr->payloads)
|
if (!mgr->payloads)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
@ -2854,7 +2937,9 @@ int drm_dp_mst_topology_mgr_init(struct drm_dp_mst_topology_mgr *mgr,
|
||||||
if (!mgr->proposed_vcpis)
|
if (!mgr->proposed_vcpis)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
set_bit(0, &mgr->payload_mask);
|
set_bit(0, &mgr->payload_mask);
|
||||||
test_calc_pbn_mode();
|
if (test_calc_pbn_mode() < 0)
|
||||||
|
DRM_ERROR("MST PBN self-test failed\n");
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
|
EXPORT_SYMBOL(drm_dp_mst_topology_mgr_init);
|
||||||
|
|
|
@ -44,8 +44,6 @@ struct drm_dp_vcpi {
|
||||||
/**
|
/**
|
||||||
* struct drm_dp_mst_port - MST port
|
* struct drm_dp_mst_port - MST port
|
||||||
* @kref: reference count for this port.
|
* @kref: reference count for this port.
|
||||||
* @guid_valid: for DP 1.2 devices if we have validated the GUID.
|
|
||||||
* @guid: guid for DP 1.2 device on this port.
|
|
||||||
* @port_num: port number
|
* @port_num: port number
|
||||||
* @input: if this port is an input port.
|
* @input: if this port is an input port.
|
||||||
* @mcs: message capability status - DP 1.2 spec.
|
* @mcs: message capability status - DP 1.2 spec.
|
||||||
|
@ -70,10 +68,6 @@ struct drm_dp_vcpi {
|
||||||
struct drm_dp_mst_port {
|
struct drm_dp_mst_port {
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
|
|
||||||
/* if dpcd 1.2 device is on this port - its GUID info */
|
|
||||||
bool guid_valid;
|
|
||||||
u8 guid[16];
|
|
||||||
|
|
||||||
u8 port_num;
|
u8 port_num;
|
||||||
bool input;
|
bool input;
|
||||||
bool mcs;
|
bool mcs;
|
||||||
|
@ -110,10 +104,12 @@ struct drm_dp_mst_port {
|
||||||
* @tx_slots: transmission slots for this device.
|
* @tx_slots: transmission slots for this device.
|
||||||
* @last_seqno: last sequence number used to talk to this.
|
* @last_seqno: last sequence number used to talk to this.
|
||||||
* @link_address_sent: if a link address message has been sent to this device yet.
|
* @link_address_sent: if a link address message has been sent to this device yet.
|
||||||
|
* @guid: guid for DP 1.2 branch device. port under this branch can be
|
||||||
|
* identified by port #.
|
||||||
*
|
*
|
||||||
* This structure represents an MST branch device, there is one
|
* This structure represents an MST branch device, there is one
|
||||||
* primary branch device at the root, along with any others connected
|
* primary branch device at the root, along with any other branches connected
|
||||||
* to downstream ports
|
* to downstream port of parent branches.
|
||||||
*/
|
*/
|
||||||
struct drm_dp_mst_branch {
|
struct drm_dp_mst_branch {
|
||||||
struct kref kref;
|
struct kref kref;
|
||||||
|
@ -132,6 +128,9 @@ struct drm_dp_mst_branch {
|
||||||
struct drm_dp_sideband_msg_tx *tx_slots[2];
|
struct drm_dp_sideband_msg_tx *tx_slots[2];
|
||||||
int last_seqno;
|
int last_seqno;
|
||||||
bool link_address_sent;
|
bool link_address_sent;
|
||||||
|
|
||||||
|
/* global unique identifier to identify branch devices */
|
||||||
|
u8 guid[16];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -406,11 +405,9 @@ struct drm_dp_payload {
|
||||||
* @conn_base_id: DRM connector ID this mgr is connected to.
|
* @conn_base_id: DRM connector ID this mgr is connected to.
|
||||||
* @down_rep_recv: msg receiver state for down replies.
|
* @down_rep_recv: msg receiver state for down replies.
|
||||||
* @up_req_recv: msg receiver state for up requests.
|
* @up_req_recv: msg receiver state for up requests.
|
||||||
* @lock: protects mst state, primary, guid, dpcd.
|
* @lock: protects mst state, primary, dpcd.
|
||||||
* @mst_state: if this manager is enabled for an MST capable port.
|
* @mst_state: if this manager is enabled for an MST capable port.
|
||||||
* @mst_primary: pointer to the primary branch device.
|
* @mst_primary: pointer to the primary branch device.
|
||||||
* @guid_valid: GUID valid for the primary branch device.
|
|
||||||
* @guid: GUID for primary port.
|
|
||||||
* @dpcd: cache of DPCD for primary port.
|
* @dpcd: cache of DPCD for primary port.
|
||||||
* @pbn_div: PBN to slots divisor.
|
* @pbn_div: PBN to slots divisor.
|
||||||
*
|
*
|
||||||
|
@ -432,13 +429,11 @@ struct drm_dp_mst_topology_mgr {
|
||||||
struct drm_dp_sideband_msg_rx up_req_recv;
|
struct drm_dp_sideband_msg_rx up_req_recv;
|
||||||
|
|
||||||
/* pointer to info about the initial MST device */
|
/* pointer to info about the initial MST device */
|
||||||
struct mutex lock; /* protects mst_state + primary + guid + dpcd */
|
struct mutex lock; /* protects mst_state + primary + dpcd */
|
||||||
|
|
||||||
bool mst_state;
|
bool mst_state;
|
||||||
struct drm_dp_mst_branch *mst_primary;
|
struct drm_dp_mst_branch *mst_primary;
|
||||||
/* primary MST device GUID */
|
|
||||||
bool guid_valid;
|
|
||||||
u8 guid[16];
|
|
||||||
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
u8 dpcd[DP_RECEIVER_CAP_SIZE];
|
||||||
u8 sink_count;
|
u8 sink_count;
|
||||||
int pbn_div;
|
int pbn_div;
|
||||||
|
|
|
@ -73,18 +73,28 @@ static inline u32 dfixed_div(fixed20_12 A, fixed20_12 B)
|
||||||
#define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT)
|
#define DRM_FIXED_ONE (1ULL << DRM_FIXED_POINT)
|
||||||
#define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)
|
#define DRM_FIXED_DECIMAL_MASK (DRM_FIXED_ONE - 1)
|
||||||
#define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK)
|
#define DRM_FIXED_DIGITS_MASK (~DRM_FIXED_DECIMAL_MASK)
|
||||||
|
#define DRM_FIXED_EPSILON 1LL
|
||||||
|
#define DRM_FIXED_ALMOST_ONE (DRM_FIXED_ONE - DRM_FIXED_EPSILON)
|
||||||
|
|
||||||
static inline s64 drm_int2fixp(int a)
|
static inline s64 drm_int2fixp(int a)
|
||||||
{
|
{
|
||||||
return ((s64)a) << DRM_FIXED_POINT;
|
return ((s64)a) << DRM_FIXED_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int drm_fixp2int(int64_t a)
|
static inline int drm_fixp2int(s64 a)
|
||||||
{
|
{
|
||||||
return ((s64)a) >> DRM_FIXED_POINT;
|
return ((s64)a) >> DRM_FIXED_POINT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned drm_fixp_msbset(int64_t a)
|
static inline int drm_fixp2int_ceil(s64 a)
|
||||||
|
{
|
||||||
|
if (a > 0)
|
||||||
|
return drm_fixp2int(a + DRM_FIXED_ALMOST_ONE);
|
||||||
|
else
|
||||||
|
return drm_fixp2int(a - DRM_FIXED_ALMOST_ONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned drm_fixp_msbset(s64 a)
|
||||||
{
|
{
|
||||||
unsigned shift, sign = (a >> 63) & 1;
|
unsigned shift, sign = (a >> 63) & 1;
|
||||||
|
|
||||||
|
@ -136,6 +146,45 @@ static inline s64 drm_fixp_div(s64 a, s64 b)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline s64 drm_fixp_from_fraction(s64 a, s64 b)
|
||||||
|
{
|
||||||
|
s64 res;
|
||||||
|
bool a_neg = a < 0;
|
||||||
|
bool b_neg = b < 0;
|
||||||
|
u64 a_abs = a_neg ? -a : a;
|
||||||
|
u64 b_abs = b_neg ? -b : b;
|
||||||
|
u64 rem;
|
||||||
|
|
||||||
|
/* determine integer part */
|
||||||
|
u64 res_abs = div64_u64_rem(a_abs, b_abs, &rem);
|
||||||
|
|
||||||
|
/* determine fractional part */
|
||||||
|
{
|
||||||
|
u32 i = DRM_FIXED_POINT;
|
||||||
|
|
||||||
|
do {
|
||||||
|
rem <<= 1;
|
||||||
|
res_abs <<= 1;
|
||||||
|
if (rem >= b_abs) {
|
||||||
|
res_abs |= 1;
|
||||||
|
rem -= b_abs;
|
||||||
|
}
|
||||||
|
} while (--i != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* round up LSB */
|
||||||
|
{
|
||||||
|
u64 summand = (rem << 1) >= b_abs;
|
||||||
|
|
||||||
|
res_abs += summand;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = (s64) res_abs;
|
||||||
|
if (a_neg ^ b_neg)
|
||||||
|
res = -res;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static inline s64 drm_fixp_exp(s64 x)
|
static inline s64 drm_fixp_exp(s64 x)
|
||||||
{
|
{
|
||||||
s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);
|
s64 tolerance = div64_s64(DRM_FIXED_ONE, 1000000);
|
||||||
|
|
Loading…
Reference in a new issue