]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_dp_helper.c
drm/dp: Use large transactions for I2C over AUX
[karo-tx-linux.git] / drivers / gpu / drm / drm_dp_helper.c
index f1283878ff6df3000608773de43ee22e77cd7b59..d5368ea56a0fc12e4265d4e1f52cd87816514e20 100644 (file)
@@ -427,11 +427,13 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
  * retrying the transaction as appropriate.  It is assumed that the
  * aux->transfer function does not modify anything in the msg other than the
  * reply field.
+ *
+ * Returns bytes transferred on success, or a negative error code on failure.
  */
 static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
 {
        unsigned int retry;
-       int err;
+       int ret;
 
        /*
         * DP1.2 sections 2.7.7.1.5.6.1 and 2.7.7.1.6.6.1: A DP Source device
@@ -440,14 +442,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
         */
        for (retry = 0; retry < 7; retry++) {
                mutex_lock(&aux->hw_mutex);
-               err = aux->transfer(aux, msg);
+               ret = aux->transfer(aux, msg);
                mutex_unlock(&aux->hw_mutex);
-               if (err < 0) {
-                       if (err == -EBUSY)
+               if (ret < 0) {
+                       if (ret == -EBUSY)
                                continue;
 
-                       DRM_DEBUG_KMS("transaction failed: %d\n", err);
-                       return err;
+                       DRM_DEBUG_KMS("transaction failed: %d\n", ret);
+                       return ret;
                }
 
 
@@ -488,9 +490,7 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                         * Both native ACK and I2C ACK replies received. We
                         * can assume the transfer was successful.
                         */
-                       if (err < msg->size)
-                               return -EPROTO;
-                       return 0;
+                       return ret;
 
                case DP_AUX_I2C_REPLY_NACK:
                        DRM_DEBUG_KMS("I2C nack\n");
@@ -513,14 +513,55 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        return -EREMOTEIO;
 }
 
+/*
+ * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
+ *
+ * Returns an error code on failure, or a recommended transfer size on success.
+ */
+static int drm_dp_i2c_drain_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *orig_msg)
+{
+       int err, ret = orig_msg->size;
+       struct drm_dp_aux_msg msg = *orig_msg;
+
+       while (msg.size > 0) {
+               err = drm_dp_i2c_do_msg(aux, &msg);
+               if (err <= 0)
+                       return err == 0 ? -EPROTO : err;
+
+               if (err < msg.size && err < ret) {
+                       DRM_DEBUG_KMS("Partial I2C reply: requested %zu bytes got %d bytes\n",
+                                     msg.size, err);
+                       ret = err;
+               }
+
+               msg.size -= err;
+               msg.buffer += err;
+       }
+
+       return ret;
+}
+
+/*
+ * Bizlink designed DP->DVI-D Dual Link adapters require the I2C over AUX
+ * packets to be as large as possible. If not, the I2C transactions never
+ * succeed. Hence the default is maximum.
+ */
+static int dp_aux_i2c_transfer_size __read_mostly = DP_AUX_MAX_PAYLOAD_BYTES;
+module_param_unsafe(dp_aux_i2c_transfer_size, int, 0644);
+MODULE_PARM_DESC(dp_aux_i2c_transfer_size,
+                "Number of bytes to transfer in a single I2C over DP AUX CH message, (1-16, default 16)");
+
 static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                           int num)
 {
        struct drm_dp_aux *aux = adapter->algo_data;
        unsigned int i, j;
+       unsigned transfer_size;
        struct drm_dp_aux_msg msg;
        int err = 0;
 
+       dp_aux_i2c_transfer_size = clamp(dp_aux_i2c_transfer_size, 1, DP_AUX_MAX_PAYLOAD_BYTES);
+
        memset(&msg, 0, sizeof(msg));
 
        for (i = 0; i < num; i++) {
@@ -538,20 +579,19 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                err = drm_dp_i2c_do_msg(aux, &msg);
                if (err < 0)
                        break;
-               /*
-                * Many hardware implementations support FIFOs larger than a
-                * single byte, but it has been empirically determined that
-                * transferring data in larger chunks can actually lead to
-                * decreased performance. Therefore each message is simply
-                * transferred byte-by-byte.
+               /* We want each transaction to be as large as possible, but
+                * we'll go to smaller sizes if the hardware gives us a
+                * short reply.
                 */
-               for (j = 0; j < msgs[i].len; j++) {
+               transfer_size = dp_aux_i2c_transfer_size;
+               for (j = 0; j < msgs[i].len; j += msg.size) {
                        msg.buffer = msgs[i].buf + j;
-                       msg.size = 1;
+                       msg.size = min(transfer_size, msgs[i].len - j);
 
-                       err = drm_dp_i2c_do_msg(aux, &msg);
+                       err = drm_dp_i2c_drain_msg(aux, &msg);
                        if (err < 0)
                                break;
+                       transfer_size = err;
                }
                if (err < 0)
                        break;