]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - drivers/gpu/drm/drm_dp_mst_topology.c
drm/dp_mst: Add PBN calculation for DSC modes
[linux.git] / drivers / gpu / drm / drm_dp_mst_topology.c
index ae5809a1f19aac2d23dc318daedb8f343833d4f0..2443532341d00209182e361b264da00aac8b3418 100644 (file)
@@ -76,6 +76,11 @@ static int drm_dp_send_dpcd_write(struct drm_dp_mst_topology_mgr *mgr,
 
 static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
                                    struct drm_dp_mst_branch *mstb);
+
+static void
+drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
+                                  struct drm_dp_mst_branch *mstb);
+
 static int drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
                                           struct drm_dp_mst_branch *mstb,
                                           struct drm_dp_mst_port *port);
@@ -517,8 +522,10 @@ drm_dp_decode_sideband_req(const struct drm_dp_sideband_msg_tx *raw,
                        }
 
                        if (failed) {
-                               for (i = 0; i < r->num_transactions; i++)
+                               for (i = 0; i < r->num_transactions; i++) {
+                                       tx = &r->transactions[i];
                                        kfree(tx->bytes);
+                               }
                                return -ENOMEM;
                        }
 
@@ -950,6 +957,8 @@ static bool drm_dp_sideband_parse_reply(struct drm_dp_sideband_msg_rx *raw,
        case DP_POWER_DOWN_PHY:
        case DP_POWER_UP_PHY:
                return drm_dp_sideband_parse_power_updown_phy_ack(raw, msg);
+       case DP_CLEAR_PAYLOAD_ID_TABLE:
+               return true; /* since there's nothing to parse */
        default:
                DRM_ERROR("Got unknown reply 0x%02x (%s)\n", msg->req_type,
                          drm_dp_mst_req_type_str(msg->req_type));
@@ -1048,6 +1057,15 @@ static int build_link_address(struct drm_dp_sideband_msg_tx *msg)
        return 0;
 }
 
+static int build_clear_payload_id_table(struct drm_dp_sideband_msg_tx *msg)
+{
+       struct drm_dp_sideband_msg_req_body req;
+
+       req.req_type = DP_CLEAR_PAYLOAD_ID_TABLE;
+       drm_dp_encode_sideband_req(&req, msg);
+       return 0;
+}
+
 static int build_enum_path_resources(struct drm_dp_sideband_msg_tx *msg, int port_num)
 {
        struct drm_dp_sideband_msg_req_body req;
@@ -2520,10 +2538,14 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work)
        struct drm_device *dev = mgr->dev;
        struct drm_dp_mst_branch *mstb;
        int ret;
+       bool clear_payload_id_table;
 
        mutex_lock(&mgr->probe_lock);
 
        mutex_lock(&mgr->lock);
+       clear_payload_id_table = !mgr->payload_id_table_cleared;
+       mgr->payload_id_table_cleared = true;
+
        mstb = mgr->mst_primary;
        if (mstb) {
                ret = drm_dp_mst_topology_try_get_mstb(mstb);
@@ -2536,6 +2558,19 @@ static void drm_dp_mst_link_probe_work(struct work_struct *work)
                return;
        }
 
+       /*
+        * Certain branch devices seem to incorrectly report an available_pbn
+        * of 0 on downstream sinks, even after clearing the
+        * DP_PAYLOAD_ALLOCATE_* registers in
+        * drm_dp_mst_topology_mgr_set_mst(). Namely, the CableMatters USB-C
+        * 2x DP hub. Sending a CLEAR_PAYLOAD_ID_TABLE message seems to make
+        * things work again.
+        */
+       if (clear_payload_id_table) {
+               DRM_DEBUG_KMS("Clearing payload ID table\n");
+               drm_dp_send_clear_payload_id_table(mgr, mstb);
+       }
+
        ret = drm_dp_check_and_send_link_address(mgr, mstb);
        drm_dp_mst_topology_put_mstb(mstb);
 
@@ -2859,6 +2894,28 @@ static int drm_dp_send_link_address(struct drm_dp_mst_topology_mgr *mgr,
        return ret < 0 ? ret : changed;
 }
 
+void drm_dp_send_clear_payload_id_table(struct drm_dp_mst_topology_mgr *mgr,
+                                       struct drm_dp_mst_branch *mstb)
+{
+       struct drm_dp_sideband_msg_tx *txmsg;
+       int len, ret;
+
+       txmsg = kzalloc(sizeof(*txmsg), GFP_KERNEL);
+       if (!txmsg)
+               return;
+
+       txmsg->dst = mstb;
+       len = build_clear_payload_id_table(txmsg);
+
+       drm_dp_queue_down_tx(mgr, txmsg);
+
+       ret = drm_dp_mst_wait_tx_reply(mstb, txmsg);
+       if (ret > 0 && txmsg->reply.reply_type == DP_SIDEBAND_REPLY_NAK)
+               DRM_DEBUG_KMS("clear payload table id nak received\n");
+
+       kfree(txmsg);
+}
+
 static int
 drm_dp_send_enum_path_resources(struct drm_dp_mst_topology_mgr *mgr,
                                struct drm_dp_mst_branch *mstb,
@@ -3176,9 +3233,11 @@ int drm_dp_update_payload_part1(struct drm_dp_mst_topology_mgr *mgr)
                        drm_dp_mst_topology_put_port(port);
        }
 
-       for (i = 0; i < mgr->max_payloads; i++) {
-               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL)
+       for (i = 0; i < mgr->max_payloads; /* do nothing */) {
+               if (mgr->payloads[i].payload_state != DP_PAYLOAD_DELETE_LOCAL) {
+                       i++;
                        continue;
+               }
 
                DRM_DEBUG_KMS("removing payload %d\n", i);
                for (j = i; j < mgr->max_payloads - 1; j++) {
@@ -3386,6 +3445,7 @@ static int drm_dp_get_vc_payload_bw(u8 dp_link_bw, u8  dp_link_count)
 int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool mst_state)
 {
        int ret = 0;
+       int i = 0;
        struct drm_dp_mst_branch *mstb = NULL;
 
        mutex_lock(&mgr->lock);
@@ -3446,10 +3506,23 @@ int drm_dp_mst_topology_mgr_set_mst(struct drm_dp_mst_topology_mgr *mgr, bool ms
                /* this can fail if the device is gone */
                drm_dp_dpcd_writeb(mgr->aux, DP_MSTM_CTRL, 0);
                ret = 0;
+               mutex_lock(&mgr->payload_lock);
                memset(mgr->payloads, 0, mgr->max_payloads * sizeof(struct drm_dp_payload));
                mgr->payload_mask = 0;
                set_bit(0, &mgr->payload_mask);
+               for (i = 0; i < mgr->max_payloads; i++) {
+                       struct drm_dp_vcpi *vcpi = mgr->proposed_vcpis[i];
+
+                       if (vcpi) {
+                               vcpi->vcpi = 0;
+                               vcpi->num_slots = 0;
+                       }
+                       mgr->proposed_vcpis[i] = NULL;
+               }
                mgr->vcpi_mask = 0;
+               mutex_unlock(&mgr->payload_lock);
+
+               mgr->payload_id_table_cleared = false;
        }
 
 out_unlock:
@@ -4342,10 +4415,11 @@ EXPORT_SYMBOL(drm_dp_check_act_status);
  * drm_dp_calc_pbn_mode() - Calculate the PBN for a mode.
  * @clock: dot clock for the mode
  * @bpp: bpp for the mode.
+ * @dsc: DSC mode. If true, bpp has units of 1/16 of a bit per pixel
  *
  * This uses the formula in the spec to calculate the PBN value for a mode.
  */
-int drm_dp_calc_pbn_mode(int clock, int bpp)
+int drm_dp_calc_pbn_mode(int clock, int bpp, bool dsc)
 {
        /*
         * margin 5300ppm + 300ppm ~ 0.6% as per spec, factor is 1.006
@@ -4356,7 +4430,16 @@ int drm_dp_calc_pbn_mode(int clock, int bpp)
         * peak_kbps *= (1006/1000)
         * peak_kbps *= (64/54)
         * peak_kbps *= 8    convert to bytes
+        *
+        * If the bpp is in units of 1/16, further divide by 16. Put this
+        * factor in the numerator rather than the denominator to avoid
+        * integer overflow
         */
+
+       if (dsc)
+               return DIV_ROUND_UP_ULL(mul_u32_u32(clock * (bpp / 16), 64 * 1006),
+                                       8 * 54 * 1000 * 1000);
+
        return DIV_ROUND_UP_ULL(mul_u32_u32(clock * bpp, 64 * 1006),
                                8 * 54 * 1000 * 1000);
 }