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:
Dave Airlie 2016-02-05 15:24:17 +10:00
commit 6739b3d7bc
3 changed files with 239 additions and 110 deletions

View file

@ -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);

View file

@ -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;

View file

@ -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);