]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/gpu/drm/drm_dp_helper.c
Merge tag 'v4.3-rc2' into topic/drm-misc
[karo-tx-linux.git] / drivers / gpu / drm / drm_dp_helper.c
index 291734e87fca7457da9eb3ec8e0ba80f262bd232..9535c5b60387281a8a95929c09201dd086fff9cc 100644 (file)
@@ -424,6 +424,19 @@ static u32 drm_dp_i2c_functionality(struct i2c_adapter *adapter)
               I2C_FUNC_10BIT_ADDR;
 }
 
+static void drm_dp_i2c_msg_write_status_update(struct drm_dp_aux_msg *msg)
+{
+       /*
+        * In case of i2c defer or short i2c ack reply to a write,
+        * we need to switch to WRITE_STATUS_UPDATE to drain the
+        * rest of the message
+        */
+       if ((msg->request & ~DP_AUX_I2C_MOT) == DP_AUX_I2C_WRITE) {
+               msg->request &= DP_AUX_I2C_MOT;
+               msg->request |= DP_AUX_I2C_WRITE_STATUS_UPDATE;
+       }
+}
+
 #define AUX_PRECHARGE_LEN 10 /* 10 to 16 */
 #define AUX_SYNC_LEN (16 + 4) /* preamble + AUX_SYNC_END */
 #define AUX_STOP_LEN 4
@@ -579,6 +592,8 @@ 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 (ret != msg->size)
+                               drm_dp_i2c_msg_write_status_update(msg);
                        return ret;
 
                case DP_AUX_I2C_REPLY_NACK:
@@ -596,6 +611,8 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
                        if (defer_i2c < 7)
                                defer_i2c++;
                        usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
+                       drm_dp_i2c_msg_write_status_update(msg);
+
                        continue;
 
                default:
@@ -608,6 +625,14 @@ static int drm_dp_i2c_do_msg(struct drm_dp_aux *aux, struct drm_dp_aux_msg *msg)
        return -EREMOTEIO;
 }
 
+static void drm_dp_i2c_msg_set_request(struct drm_dp_aux_msg *msg,
+                                      const struct i2c_msg *i2c_msg)
+{
+       msg->request = (i2c_msg->flags & I2C_M_RD) ?
+               DP_AUX_I2C_READ : DP_AUX_I2C_WRITE;
+       msg->request |= DP_AUX_I2C_MOT;
+}
+
 /*
  * Keep retrying drm_dp_i2c_do_msg until all data has been transferred.
  *
@@ -661,10 +686,7 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
 
        for (i = 0; i < num; i++) {
                msg.address = msgs[i].addr;
-               msg.request = (msgs[i].flags & I2C_M_RD) ?
-                       DP_AUX_I2C_READ :
-                       DP_AUX_I2C_WRITE;
-               msg.request |= DP_AUX_I2C_MOT;
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
                /* Send a bare address packet to start the transaction.
                 * Zero sized messages specify an address only (bare
                 * address) transaction.
@@ -672,6 +694,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                msg.buffer = NULL;
                msg.size = 0;
                err = drm_dp_i2c_do_msg(aux, &msg);
+
+               /*
+                * Reset msg.request in case in case it got
+                * changed into a WRITE_STATUS_UPDATE.
+                */
+               drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                if (err < 0)
                        break;
                /* We want each transaction to be as large as possible, but
@@ -684,6 +713,13 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
                        msg.size = min(transfer_size, msgs[i].len - j);
 
                        err = drm_dp_i2c_drain_msg(aux, &msg);
+
+                       /*
+                        * Reset msg.request in case in case it got
+                        * changed into a WRITE_STATUS_UPDATE.
+                        */
+                       drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
+
                        if (err < 0)
                                break;
                        transfer_size = err;