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

omapdrm/dss/hdmi4_cec: fix interrupt handling

The omap4 CEC hardware cannot tell a Nack from a Low Drive from an
Arbitration Lost error, so just report a Nack, which is almost
certainly the reason for the error anyway.

This also simplifies the implementation. The only three interrupts
that need to be enabled are:

Transmit Buffer Full/Empty Change event: triggered when the
transmit finished successfully and cleared the buffer.

Receiver FIFO Not Empty event: triggered when a message was received.

Frame Retransmit Count Exceeded event: triggered when a transmit
failed repeatedly, usually due to the message being Nacked. Other
reasons are possible (Low Drive, Arbitration Lost) but there is no
way to know. If this happens the TX buffer needs to be cleared
manually.

While testing various error conditions I noticed that the hardware
can receive messages up to 18 bytes in total, which exceeds the legal
maximum of 16. This could cause a buffer overflow, so we check for
this and constrain the size to 16 bytes.

The old incorrect interrupt handler could cause the CEC framework to
enter into a bad state because it mis-detected the "Start Bit Irregularity
event" as an ARB_LOST transmit error when it actually is a receive error
which should be ignored.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Reported-by: Henrik Austad <haustad@cisco.com>
Tested-by: Henrik Austad <haustad@cisco.com>
Tested-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Tomi Valkeinen <tomi.valkeinen@ti.com>

authored by

Hans Verkuil and committed by
Tomi Valkeinen
df29c9db 03bfd4e1

+9 -37
+9 -37
drivers/gpu/drm/omapdrm/dss/hdmi4_cec.c
··· 78 78 79 79 /* then read the message */ 80 80 msg.len = cnt & 0xf; 81 + if (msg.len > CEC_MAX_MSG_SIZE - 2) 82 + msg.len = CEC_MAX_MSG_SIZE - 2; 81 83 msg.msg[0] = hdmi_read_reg(core->base, 82 84 HDMI_CEC_RX_CMD_HEADER); 83 85 msg.msg[1] = hdmi_read_reg(core->base, ··· 106 104 } 107 105 } 108 106 109 - static void hdmi_cec_transmit_fifo_empty(struct hdmi_core_data *core, u32 stat1) 110 - { 111 - if (stat1 & 2) { 112 - u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); 113 - 114 - cec_transmit_done(core->adap, 115 - CEC_TX_STATUS_NACK | 116 - CEC_TX_STATUS_MAX_RETRIES, 117 - 0, (dbg3 >> 4) & 7, 0, 0); 118 - } else if (stat1 & 1) { 119 - cec_transmit_done(core->adap, 120 - CEC_TX_STATUS_ARB_LOST | 121 - CEC_TX_STATUS_MAX_RETRIES, 122 - 0, 0, 0, 0); 123 - } else if (stat1 == 0) { 124 - cec_transmit_done(core->adap, CEC_TX_STATUS_OK, 125 - 0, 0, 0, 0); 126 - } 127 - } 128 - 129 107 void hdmi4_cec_irq(struct hdmi_core_data *core) 130 108 { 131 109 u32 stat0 = hdmi_read_reg(core->base, HDMI_CEC_INT_STATUS_0); ··· 114 132 hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_0, stat0); 115 133 hdmi_write_reg(core->base, HDMI_CEC_INT_STATUS_1, stat1); 116 134 117 - if (stat0 & 0x40) 135 + if (stat0 & 0x20) { 136 + cec_transmit_done(core->adap, CEC_TX_STATUS_OK, 137 + 0, 0, 0, 0); 118 138 REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); 119 - else if (stat0 & 0x24) 120 - hdmi_cec_transmit_fifo_empty(core, stat1); 121 - if (stat1 & 2) { 139 + } else if (stat1 & 0x02) { 122 140 u32 dbg3 = hdmi_read_reg(core->base, HDMI_CEC_DBG_3); 123 141 124 142 cec_transmit_done(core->adap, 125 143 CEC_TX_STATUS_NACK | 126 144 CEC_TX_STATUS_MAX_RETRIES, 127 145 0, (dbg3 >> 4) & 7, 0, 0); 128 - } else if (stat1 & 1) { 129 - cec_transmit_done(core->adap, 130 - CEC_TX_STATUS_ARB_LOST | 131 - CEC_TX_STATUS_MAX_RETRIES, 132 - 0, 0, 0, 0); 146 + REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); 133 147 } 134 148 if (stat0 & 0x02) 135 149 hdmi_cec_received_msg(core); 136 - if (stat1 & 0x3) 137 - REG_FLD_MOD(core->base, HDMI_CEC_DBG_3, 0x1, 7, 7); 138 150 } 139 151 140 152 static bool hdmi_cec_clear_tx_fifo(struct cec_adapter *adap) ··· 207 231 /* 208 232 * Enable CEC interrupts: 209 233 * Transmit Buffer Full/Empty Change event 210 - * Transmitter FIFO Empty event 211 234 * Receiver FIFO Not Empty event 212 235 */ 213 - hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x26); 236 + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_0, 0x22); 214 237 /* 215 238 * Enable CEC interrupts: 216 - * RX FIFO Overrun Error event 217 - * Short Pulse Detected event 218 239 * Frame Retransmit Count Exceeded event 219 - * Start Bit Irregularity event 220 240 */ 221 - hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x0f); 241 + hdmi_write_reg(core->base, HDMI_CEC_INT_ENABLE_1, 0x02); 222 242 223 243 /* cec calibration enable (self clearing) */ 224 244 hdmi_write_reg(core->base, HDMI_CEC_SETUP, 0x03);