Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

drm/tegra: dp: Support address-only I2C-over-AUX transactions

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>

authored by

Thierry Reding and committed by
Christian König
1ca20305 c39b0695

+32 -13
+31 -13
drivers/gpu/drm/tegra/dpaux.c
··· 99 99 static ssize_t tegra_dpaux_transfer(struct drm_dp_aux *aux, 100 100 struct drm_dp_aux_msg *msg) 101 101 { 102 - unsigned long value = DPAUX_DP_AUXCTL_TRANSACTREQ; 103 102 unsigned long timeout = msecs_to_jiffies(250); 104 103 struct tegra_dpaux *dpaux = to_dpaux(aux); 105 104 unsigned long status; 106 105 ssize_t ret = 0; 106 + u32 value; 107 107 108 - if (msg->size < 1 || msg->size > 16) 108 + /* Tegra has 4x4 byte DP AUX transmit and receive FIFOs. */ 109 + if (msg->size > 16) 109 110 return -EINVAL; 110 111 111 - tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR); 112 + /* 113 + * Allow zero-sized messages only for I2C, in which case they specify 114 + * address-only transactions. 115 + */ 116 + if (msg->size < 1) { 117 + switch (msg->request & ~DP_AUX_I2C_MOT) { 118 + case DP_AUX_I2C_WRITE: 119 + case DP_AUX_I2C_READ: 120 + value = DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY; 121 + break; 122 + 123 + default: 124 + return -EINVAL; 125 + } 126 + } else { 127 + /* For non-zero-sized messages, set the CMDLEN field. */ 128 + value = DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1); 129 + } 112 130 113 131 switch (msg->request & ~DP_AUX_I2C_MOT) { 114 132 case DP_AUX_I2C_WRITE: 115 133 if (msg->request & DP_AUX_I2C_MOT) 116 - value = DPAUX_DP_AUXCTL_CMD_MOT_WR; 134 + value |= DPAUX_DP_AUXCTL_CMD_MOT_WR; 117 135 else 118 - value = DPAUX_DP_AUXCTL_CMD_I2C_WR; 136 + value |= DPAUX_DP_AUXCTL_CMD_I2C_WR; 119 137 120 138 break; 121 139 122 140 case DP_AUX_I2C_READ: 123 141 if (msg->request & DP_AUX_I2C_MOT) 124 - value = DPAUX_DP_AUXCTL_CMD_MOT_RD; 142 + value |= DPAUX_DP_AUXCTL_CMD_MOT_RD; 125 143 else 126 - value = DPAUX_DP_AUXCTL_CMD_I2C_RD; 144 + value |= DPAUX_DP_AUXCTL_CMD_I2C_RD; 127 145 128 146 break; 129 147 130 148 case DP_AUX_I2C_STATUS: 131 149 if (msg->request & DP_AUX_I2C_MOT) 132 - value = DPAUX_DP_AUXCTL_CMD_MOT_RQ; 150 + value |= DPAUX_DP_AUXCTL_CMD_MOT_RQ; 133 151 else 134 - value = DPAUX_DP_AUXCTL_CMD_I2C_RQ; 152 + value |= DPAUX_DP_AUXCTL_CMD_I2C_RQ; 135 153 136 154 break; 137 155 138 156 case DP_AUX_NATIVE_WRITE: 139 - value = DPAUX_DP_AUXCTL_CMD_AUX_WR; 157 + value |= DPAUX_DP_AUXCTL_CMD_AUX_WR; 140 158 break; 141 159 142 160 case DP_AUX_NATIVE_READ: 143 - value = DPAUX_DP_AUXCTL_CMD_AUX_RD; 161 + value |= DPAUX_DP_AUXCTL_CMD_AUX_RD; 144 162 break; 145 163 146 164 default: 147 165 return -EINVAL; 148 166 } 149 167 150 - value |= DPAUX_DP_AUXCTL_CMDLEN(msg->size - 1); 168 + tegra_dpaux_writel(dpaux, msg->address, DPAUX_DP_AUXADDR); 151 169 tegra_dpaux_writel(dpaux, value, DPAUX_DP_AUXCTL); 152 170 153 171 if ((msg->request & DP_AUX_I2C_READ) == 0) { ··· 216 198 break; 217 199 } 218 200 219 - if (msg->reply == DP_AUX_NATIVE_REPLY_ACK) { 201 + if ((msg->size > 0) && (msg->reply == DP_AUX_NATIVE_REPLY_ACK)) { 220 202 if (msg->request & DP_AUX_I2C_READ) { 221 203 size_t count = value & DPAUX_DP_AUXSTAT_REPLY_MASK; 222 204
+1
drivers/gpu/drm/tegra/dpaux.h
··· 32 32 #define DPAUX_DP_AUXCTL_CMD_I2C_RQ (2 << 12) 33 33 #define DPAUX_DP_AUXCTL_CMD_I2C_RD (1 << 12) 34 34 #define DPAUX_DP_AUXCTL_CMD_I2C_WR (0 << 12) 35 + #define DPAUX_DP_AUXCTL_CMD_ADDRESS_ONLY (1 << 8) 35 36 #define DPAUX_DP_AUXCTL_CMDLEN(x) ((x) & 0xff) 36 37 37 38 #define DPAUX_DP_AUXSTAT 0x31