689,7 → 689,7 |
static int drm_dp_mst_assign_payload_id(struct drm_dp_mst_topology_mgr *mgr, |
struct drm_dp_vcpi *vcpi) |
{ |
int ret; |
int ret, vcpi_ret; |
|
mutex_lock(&mgr->payload_lock); |
ret = find_first_zero_bit(&mgr->payload_mask, mgr->max_payloads + 1); |
699,8 → 699,16 |
goto out_unlock; |
} |
|
vcpi_ret = find_first_zero_bit(&mgr->vcpi_mask, mgr->max_payloads + 1); |
if (vcpi_ret > mgr->max_payloads) { |
ret = -EINVAL; |
DRM_DEBUG_KMS("out of vcpi ids %d\n", ret); |
goto out_unlock; |
} |
|
set_bit(ret, &mgr->payload_mask); |
vcpi->vcpi = ret; |
set_bit(vcpi_ret, &mgr->vcpi_mask); |
vcpi->vcpi = vcpi_ret + 1; |
mgr->proposed_vcpis[ret - 1] = vcpi; |
out_unlock: |
mutex_unlock(&mgr->payload_lock); |
708,15 → 716,23 |
} |
|
static void drm_dp_mst_put_payload_id(struct drm_dp_mst_topology_mgr *mgr, |
int id) |
int vcpi) |
{ |
if (id == 0) |
int i; |
if (vcpi == 0) |
return; |
|
mutex_lock(&mgr->payload_lock); |
DRM_DEBUG_KMS("putting payload %d\n", id); |
clear_bit(id, &mgr->payload_mask); |
mgr->proposed_vcpis[id - 1] = NULL; |
DRM_DEBUG_KMS("putting payload %d\n", vcpi); |
clear_bit(vcpi - 1, &mgr->vcpi_mask); |
|
for (i = 0; i < mgr->max_payloads; i++) { |
if (mgr->proposed_vcpis[i]) |
if (mgr->proposed_vcpis[i]->vcpi == vcpi) { |
mgr->proposed_vcpis[i] = NULL; |
clear_bit(i + 1, &mgr->payload_mask); |
} |
} |
mutex_unlock(&mgr->payload_lock); |
} |
|
830,6 → 846,8 |
|
static void drm_dp_port_teardown_pdt(struct drm_dp_mst_port *port, int old_pdt) |
{ |
struct drm_dp_mst_branch *mstb; |
|
switch (old_pdt) { |
case DP_PEER_DEVICE_DP_LEGACY_CONV: |
case DP_PEER_DEVICE_SST_SINK: |
837,8 → 855,9 |
drm_dp_mst_unregister_i2c_bus(&port->aux); |
break; |
case DP_PEER_DEVICE_MST_BRANCHING: |
drm_dp_put_mst_branch_device(port->mstb); |
mstb = port->mstb; |
port->mstb = NULL; |
drm_dp_put_mst_branch_device(mstb); |
break; |
} |
} |
849,6 → 868,8 |
struct drm_dp_mst_topology_mgr *mgr = port->mgr; |
if (!port->input) { |
port->vcpi.num_slots = 0; |
|
kfree(port->cached_edid); |
if (port->connector) |
(*port->mgr->cbs->destroy_connector)(mgr, port->connector); |
drm_dp_port_teardown_pdt(port, port->pdt); |
1002,19 → 1023,20 |
|
static void build_mst_prop_path(struct drm_dp_mst_port *port, |
struct drm_dp_mst_branch *mstb, |
char *proppath) |
char *proppath, |
size_t proppath_size) |
{ |
int i; |
char temp[8]; |
snprintf(proppath, 255, "mst:%d", mstb->mgr->conn_base_id); |
snprintf(proppath, proppath_size, "mst:%d", mstb->mgr->conn_base_id); |
for (i = 0; i < (mstb->lct - 1); i++) { |
int shift = (i % 2) ? 0 : 4; |
int port_num = mstb->rad[i / 2] >> shift; |
snprintf(temp, 8, "-%d", port_num); |
strncat(proppath, temp, 255); |
snprintf(temp, sizeof(temp), "-%d", port_num); |
strlcat(proppath, temp, proppath_size); |
} |
snprintf(temp, 8, "-%d", port->port_num); |
strncat(proppath, temp, 255); |
snprintf(temp, sizeof(temp), "-%d", port->port_num); |
strlcat(proppath, temp, proppath_size); |
} |
|
static void drm_dp_add_port(struct drm_dp_mst_branch *mstb, |
1085,9 → 1107,13 |
|
if (created && !port->input) { |
char proppath[255]; |
build_mst_prop_path(port, mstb, proppath); |
build_mst_prop_path(port, mstb, proppath, sizeof(proppath)); |
port->connector = (*mstb->mgr->cbs->add_connector)(mstb->mgr, port, proppath); |
|
if (port->port_num >= 8) { |
port->cached_edid = drm_get_edid(port->connector, &port->aux.ddc); |
} |
} |
|
/* put reference to this port */ |
drm_dp_put_port(port); |
1570,7 → 1596,7 |
} |
|
drm_dp_dpcd_write_payload(mgr, id, payload); |
payload->payload_state = 0; |
payload->payload_state = DP_PAYLOAD_DELETE_LOCAL; |
return 0; |
} |
|
1597,7 → 1623,7 |
*/ |
int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr) |
{ |
int i; |
int i, j; |
int cur_slots = 1; |
struct drm_dp_payload req_payload; |
struct drm_dp_mst_port *port; |
1614,26 → 1640,46 |
port = NULL; |
req_payload.num_slots = 0; |
} |
|
if (mgr->payloads[i].start_slot != req_payload.start_slot) { |
mgr->payloads[i].start_slot = req_payload.start_slot; |
} |
/* work out what is required to happen with this payload */ |
if (mgr->payloads[i].start_slot != req_payload.start_slot || |
mgr->payloads[i].num_slots != req_payload.num_slots) { |
if (mgr->payloads[i].num_slots != req_payload.num_slots) { |
|
/* need to push an update for this payload */ |
if (req_payload.num_slots) { |
drm_dp_create_payload_step1(mgr, i + 1, &req_payload); |
drm_dp_create_payload_step1(mgr, mgr->proposed_vcpis[i]->vcpi, &req_payload); |
mgr->payloads[i].num_slots = req_payload.num_slots; |
} else if (mgr->payloads[i].num_slots) { |
mgr->payloads[i].num_slots = 0; |
drm_dp_destroy_payload_step1(mgr, port, i + 1, &mgr->payloads[i]); |
drm_dp_destroy_payload_step1(mgr, port, port->vcpi.vcpi, &mgr->payloads[i]); |
req_payload.payload_state = mgr->payloads[i].payload_state; |
} else |
req_payload.payload_state = 0; |
|
mgr->payloads[i].start_slot = req_payload.start_slot; |
mgr->payloads[i].start_slot = 0; |
} |
mgr->payloads[i].payload_state = req_payload.payload_state; |
} |
cur_slots += req_payload.num_slots; |
} |
|
for (i = 0; i < mgr->max_payloads; i++) { |
if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { |
DRM_DEBUG_KMS("removing payload %d\n", i); |
for (j = i; j < mgr->max_payloads - 1; j++) { |
memcpy(&mgr->payloads[j], &mgr->payloads[j + 1], sizeof(struct drm_dp_payload)); |
mgr->proposed_vcpis[j] = mgr->proposed_vcpis[j + 1]; |
if (mgr->proposed_vcpis[j] && mgr->proposed_vcpis[j]->num_slots) { |
set_bit(j + 1, &mgr->payload_mask); |
} else { |
clear_bit(j + 1, &mgr->payload_mask); |
} |
} |
memset(&mgr->payloads[mgr->max_payloads - 1], 0, sizeof(struct drm_dp_payload)); |
mgr->proposed_vcpis[mgr->max_payloads - 1] = NULL; |
clear_bit(mgr->max_payloads, &mgr->payload_mask); |
|
} |
} |
mutex_unlock(&mgr->payload_lock); |
|
return 0; |
1664,9 → 1710,9 |
|
DRM_DEBUG_KMS("payload %d %d\n", i, mgr->payloads[i].payload_state); |
if (mgr->payloads[i].payload_state == DP_PAYLOAD_LOCAL) { |
ret = drm_dp_create_payload_step2(mgr, port, i + 1, &mgr->payloads[i]); |
ret = drm_dp_create_payload_step2(mgr, port, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); |
} else if (mgr->payloads[i].payload_state == DP_PAYLOAD_DELETE_LOCAL) { |
ret = drm_dp_destroy_payload_step2(mgr, i + 1, &mgr->payloads[i]); |
ret = drm_dp_destroy_payload_step2(mgr, mgr->proposed_vcpis[i]->vcpi, &mgr->payloads[i]); |
} |
if (ret) { |
mutex_unlock(&mgr->payload_lock); |
1769,17 → 1815,27 |
return 0; |
} |
|
static int drm_dp_get_vc_payload_bw(int dp_link_bw, int dp_link_count) |
static bool drm_dp_get_vc_payload_bw(int dp_link_bw, |
int dp_link_count, |
int *out) |
{ |
switch (dp_link_bw) { |
default: |
DRM_DEBUG_KMS("invalid link bandwidth in DPCD: %x (link count: %d)\n", |
dp_link_bw, dp_link_count); |
return false; |
|
case DP_LINK_BW_1_62: |
return 3 * dp_link_count; |
*out = 3 * dp_link_count; |
break; |
case DP_LINK_BW_2_7: |
return 5 * dp_link_count; |
*out = 5 * dp_link_count; |
break; |
case DP_LINK_BW_5_4: |
return 10 * dp_link_count; |
*out = 10 * dp_link_count; |
break; |
} |
return 0; |
return true; |
} |
|
/** |
1811,7 → 1867,13 |
goto out_unlock; |
} |
|
mgr->pbn_div = drm_dp_get_vc_payload_bw(mgr->dpcd[1], mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK); |
if (!drm_dp_get_vc_payload_bw(mgr->dpcd[1], |
mgr->dpcd[2] & DP_MAX_LANE_COUNT_MASK, |
&mgr->pbn_div)) { |
ret = -EINVAL; |
goto out_unlock; |
} |
|
mgr->total_pbn = 2560; |
mgr->total_slots = DIV_ROUND_UP(mgr->total_pbn, mgr->pbn_div); |
mgr->avail_slots = mgr->total_slots; |
1868,6 → 1930,7 |
memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload)); |
mgr->payload_mask = 0; |
set_bit(0, &mgr->payload_mask); |
mgr->vcpi_mask = 0; |
} |
|
out_unlock: |
2078,6 → 2141,7 |
* drm_dp_mst_hpd_irq() - MST hotplug IRQ notify |
* @mgr: manager to notify irq for. |
* @esi: 4 bytes from SINK_COUNT_ESI |
* @handled: whether the hpd interrupt was consumed or not |
* |
* This should be called from the driver when it detects a short IRQ, |
* along with the value of the DEVICE_SERVICE_IRQ_VECTOR_ESI0. The |
2119,7 → 2183,8 |
* This returns the current connection state for a port. It validates the |
* port pointer still exists so the caller doesn't require a reference |
*/ |
enum drm_connector_status drm_dp_mst_detect_port(struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) |
enum drm_connector_status drm_dp_mst_detect_port(struct drm_connector *connector, |
struct drm_dp_mst_topology_mgr *mgr, struct drm_dp_mst_port *port) |
{ |
enum drm_connector_status status = connector_status_disconnected; |
|
2138,6 → 2203,10 |
|
case DP_PEER_DEVICE_SST_SINK: |
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; |
case DP_PEER_DEVICE_DP_LEGACY_CONV: |
if (port->ldps) |
2169,7 → 2238,12 |
if (!port) |
return NULL; |
|
if (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); |
drm_dp_put_port(port); |
return edid; |
} |