]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
drm/tegra: dp: Support address-only I2C-over-AUX transactions
authorThierry Reding <treding@nvidia.com>
Mon, 7 Apr 2014 08:37:44 +0000 (10:37 +0200)
committerChristian König <christian.koenig@amd.com>
Tue, 8 Apr 2014 14:12:36 +0000 (16:12 +0200)
Certain types of I2C-over-AUX transactions require that only the address
is transferred. Detect this by looking at the AUX message's size and set
the address-only bit appropriately.

Signed-off-by: Thierry Reding <treding@nvidia.com>
drivers/gpu/drm/tegra/dpaux.c
drivers/gpu/drm/tegra/dpaux.h

index d536ed381fbd9e305951d06529d61fbad83f2685..005c19bd92dfa0e5350303d84290b27b9aae144d 100644 (file)
@@ -99,55 +99,73 @@ static void tegra_dpaux_read_fifo(struct tegra_dpaux *dpaux, u8 *buffer,
 static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
                                    struct drm_dp_aux_msg *msg)
 {
-       unsigned long value = DPAUX_DP_AUXCTL_TRANSACTREQ;
        unsigned long timeout = msecs_to_jiffies(250);
        struct tegra_dpaux *dpaux = to_dpaux(aux);
        unsigned long status;
        ssize_t ret = 0;
+       u32 value;
 
-       if (msg->size < 1 || msg->size > 16)
+       /* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */
+       if (msg->size > 16)
                return -EINVAL;
 
-       tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
+       /*
+        * Allow zero-sized messages only for I2C, in which case they specify
+        * address-only transactions.
+        */
+       if (msg->size < 1) {
+               switch (msg->request & ~DP_AUX_I2C_MOT) {
+               case DP_AUX_I2C_WRITE:
+               case DP_AUX_I2C_READ:
+                       value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY;
+                       break;
+
+               default:
+                       return -EINVAL;
+               }
+       } else {
+               /* For non-zero-sized messages, set the CMDLEN field. */
+               value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
+       }
 
        switch (msg->request & ~DP_AUX_I2C_MOT) {
        case DP_AUX_I2C_WRITE:
                if (msg->request & DP_AUX_I2C_MOT)
-                       value = DPAUX_DP_AUXCTL_CMD_MOT_WR;
+                       value |= DPAUX_DP_AUXCTL_CMD_MOT_WR;
                else
-                       value = DPAUX_DP_AUXCTL_CMD_I2C_WR;
+                       value |= DPAUX_DP_AUXCTL_CMD_I2C_WR;
 
                break;
 
        case DP_AUX_I2C_READ:
                if (msg->request & DP_AUX_I2C_MOT)
-                       value = DPAUX_DP_AUXCTL_CMD_MOT_RD;
+                       value |= DPAUX_DP_AUXCTL_CMD_MOT_RD;
                else
-                       value = DPAUX_DP_AUXCTL_CMD_I2C_RD;
+                       value |= DPAUX_DP_AUXCTL_CMD_I2C_RD;
 
                break;
 
        case DP_AUX_I2C_STATUS:
                if (msg->request & DP_AUX_I2C_MOT)
-                       value = DPAUX_DP_AUXCTL_CMD_MOT_RQ;
+                       value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ;
                else
-                       value = DPAUX_DP_AUXCTL_CMD_I2C_RQ;
+                       value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ;
 
                break;
 
        case DP_AUX_NATIVE_WRITE:
-               value = DPAUX_DP_AUXCTL_CMD_AUX_WR;
+               value |= DPAUX_DP_AUXCTL_CMD_AUX_WR;
                break;
 
        case DP_AUX_NATIVE_READ:
-               value = DPAUX_DP_AUXCTL_CMD_AUX_RD;
+               value |= DPAUX_DP_AUXCTL_CMD_AUX_RD;
                break;
 
        default:
                return -EINVAL;
        }
 
-       value |= DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1);
+       tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR);
        tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL);
 
        if ((msg->request & DP_AUX_I2C_READ) == 0) {
@@ -198,7 +216,7 @@ static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux,
                break;
        }
 
-       if (msg->reply == DP_AUX_NATIVE_REPLY_ACK) {
+       if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) {
                if (msg->request & DP_AUX_I2C_READ) {
                        size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK;
 
index 4f5bf10fdff91892b1241a4f5f3b3788cdd58554..806e245ca7874d3929e7f22227096d630d37cc13 100644 (file)
@@ -32,6 +32,7 @@
 #define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12)
 #define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12)
 #define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12)
+#define DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY (1 << 8)
 #define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff)
 
 #define DPAUX_DP_AUXSTAT 0x31