drm/i915: Don't pass pitch to intel_compute_page_offset()
[deliverable/linux.git] / drivers / gpu / drm / drm_dp_helper.c
index df64ed1c0139e9213912e0f05043489ce6f0d0ba..8f11b8741e425720b9ad25c622da7fa5b9bfbf48 100644 (file)
@@ -178,8 +178,8 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
                              unsigned int offset, void *buffer, size_t size)
 {
        struct drm_dp_aux_msg msg;
-       unsigned int retry;
-       int err = 0;
+       unsigned int retry, native_reply;
+       int err = 0, ret = 0;
 
        memset(&msg, 0, sizeof(msg));
        msg.address = offset;
@@ -196,38 +196,39 @@ static int drm_dp_dpcd_access(struct drm_dp_aux *aux, u8 request,
         * sufficient, bump to 32 which makes Dell 4k monitors happier.
         */
        for (retry = 0; retry < 32; retry++) {
-
-               err = aux->transfer(aux, &msg);
-               if (err < 0) {
-                       if (err == -EBUSY)
-                               continue;
-
-                       goto unlock;
+               if (ret != 0 && ret != -ETIMEDOUT) {
+                       usleep_range(AUX_RETRY_INTERVAL,
+                                    AUX_RETRY_INTERVAL + 100);
                }
 
+               ret = aux->transfer(aux, &msg);
 
-               switch (msg.reply & DP_AUX_NATIVE_REPLY_MASK) {
-               case DP_AUX_NATIVE_REPLY_ACK:
-                       if (err < size)
-                               err = -EPROTO;
-                       goto unlock;
-
-               case DP_AUX_NATIVE_REPLY_NACK:
-                       err = -EIO;
-                       goto unlock;
+               if (ret >= 0) {
+                       native_reply = msg.reply & DP_AUX_NATIVE_REPLY_MASK;
+                       if (native_reply == DP_AUX_NATIVE_REPLY_ACK) {
+                               if (ret == size)
+                                       goto unlock;
 
-               case DP_AUX_NATIVE_REPLY_DEFER:
-                       usleep_range(AUX_RETRY_INTERVAL, AUX_RETRY_INTERVAL + 100);
-                       break;
+                               ret = -EPROTO;
+                       } else
+                               ret = -EIO;
                }
+
+               /*
+                * We want the error we return to be the error we received on
+                * the first transaction, since we may get a different error the
+                * next time we retry
+                */
+               if (!err)
+                       err = ret;
        }
 
        DRM_DEBUG_KMS("too many retries, giving up\n");
-       err = -EIO;
+       ret = err;
 
 unlock:
        mutex_unlock(&aux->hw_mutex);
-       return err;
+       return ret;
 }
 
 /**
@@ -247,6 +248,25 @@ unlock:
 ssize_t drm_dp_dpcd_read(struct drm_dp_aux *aux, unsigned int offset,
                         void *buffer, size_t size)
 {
+       int ret;
+
+       /*
+        * HP ZR24w corrupts the first DPCD access after entering power save
+        * mode. Eg. on a read, the entire buffer will be filled with the same
+        * byte. Do a throw away read to avoid corrupting anything we care
+        * about. Afterwards things will work correctly until the monitor
+        * gets woken up and subsequently re-enters power save mode.
+        *
+        * The user pressing any button on the monitor is enough to wake it
+        * up, so there is no particularly good place to do the workaround.
+        * We just have to do it before any DPCD access and hope that the
+        * monitor doesn't power down exactly after the throw away read.
+        */
+       ret = drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, DP_DPCD_REV, buffer,
+                                1);
+       if (ret != 1)
+               return ret;
+
        return drm_dp_dpcd_access(aux, DP_AUX_NATIVE_READ, offset, buffer,
                                  size);
 }
@@ -688,8 +708,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
 
        memset(&msg, 0, sizeof(msg));
 
-       mutex_lock(&aux->hw_mutex);
-
        for (i = 0; i < num; i++) {
                msg.address = msgs[i].addr;
                drm_dp_i2c_msg_set_request(&msg, &msgs[i]);
@@ -744,8 +762,6 @@ static int drm_dp_i2c_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs,
        msg.size = 0;
        (void)drm_dp_i2c_do_msg(aux, &msg);
 
-       mutex_unlock(&aux->hw_mutex);
-
        return err;
 }
 
@@ -754,22 +770,64 @@ static const struct i2c_algorithm drm_dp_i2c_algo = {
        .master_xfer = drm_dp_i2c_xfer,
 };
 
+static struct drm_dp_aux *i2c_to_aux(struct i2c_adapter *i2c)
+{
+       return container_of(i2c, struct drm_dp_aux, ddc);
+}
+
+static void lock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       mutex_lock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static int trylock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       return mutex_trylock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
+static void unlock_bus(struct i2c_adapter *i2c, unsigned int flags)
+{
+       mutex_unlock(&i2c_to_aux(i2c)->hw_mutex);
+}
+
 /**
- * drm_dp_aux_register() - initialise and register aux channel
+ * drm_dp_aux_init() - minimally initialise an aux channel
  * @aux: DisplayPort AUX channel
  *
- * Returns 0 on success or a negative error code on failure.
+ * If you need to use the drm_dp_aux's i2c adapter prior to registering it
+ * with the outside world, call drm_dp_aux_init() first. You must still
+ * call drm_dp_aux_register() once the connector has been registered to
+ * allow userspace access to the auxiliary DP channel.
  */
-int drm_dp_aux_register(struct drm_dp_aux *aux)
+void drm_dp_aux_init(struct drm_dp_aux *aux)
 {
-       int ret;
-
        mutex_init(&aux->hw_mutex);
 
        aux->ddc.algo = &drm_dp_i2c_algo;
        aux->ddc.algo_data = aux;
        aux->ddc.retries = 3;
 
+       aux->ddc.lock_bus = lock_bus;
+       aux->ddc.trylock_bus = trylock_bus;
+       aux->ddc.unlock_bus = unlock_bus;
+}
+EXPORT_SYMBOL(drm_dp_aux_init);
+
+/**
+ * drm_dp_aux_register() - initialise and register aux channel
+ * @aux: DisplayPort AUX channel
+ *
+ * Automatically calls drm_dp_aux_init() if this hasn't been done yet.
+ *
+ * Returns 0 on success or a negative error code on failure.
+ */
+int drm_dp_aux_register(struct drm_dp_aux *aux)
+{
+       int ret;
+
+       if (!aux->ddc.algo)
+               drm_dp_aux_init(aux);
+
        aux->ddc.class = I2C_CLASS_DDC;
        aux->ddc.owner = THIS_MODULE;
        aux->ddc.dev.parent = aux->dev;
This page took 0.027234 seconds and 5 git commands to generate.