From f826ffb93b1ba0cfb9a5cfa3403813674331d4e0 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Tue, 26 Feb 2019 20:45:14 +0000
Subject: [PATCH 34/44] WIP: dw-hdmi-cec: sleep 100ms on error

---
 drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c | 18 ++++++++++++++++--
 1 file changed, 16 insertions(+), 2 deletions(-)

--- a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
+++ b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-cec.c
@@ -4,6 +4,7 @@
  *
  * Copyright (C) 2015-2017 Russell King.
  */
+#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -129,8 +130,15 @@ static irqreturn_t dw_hdmi_cec_hardirq(i
 
 	dw_hdmi_write(cec, stat, HDMI_IH_CEC_STAT0);
 
-	if (stat & CEC_STAT_ERROR_INIT) {
-		cec->tx_status = CEC_TX_STATUS_ERROR;
+	/* Status with both done and error_initiator bits have been seen
+	 * on Rockchip RK3328 devices, transmit attempt seems to have failed
+	 * when this happens, report as low drive and block cec-framework
+	 * 100ms before core retransmits the failed message, this seems to
+	 * mitigate the issue with failed transmit attempts.
+	 */
+	if ((stat & (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) == (CEC_STAT_DONE|CEC_STAT_ERROR_INIT)) {
+		pr_info("dw_hdmi_cec_hardirq: stat=%02x LOW_DRIVE\n", stat);
+		cec->tx_status = CEC_TX_STATUS_LOW_DRIVE;
 		cec->tx_done = true;
 		ret = IRQ_WAKE_THREAD;
 	} else if (stat & CEC_STAT_DONE) {
@@ -141,6 +149,10 @@ static irqreturn_t dw_hdmi_cec_hardirq(i
 		cec->tx_status = CEC_TX_STATUS_NACK;
 		cec->tx_done = true;
 		ret = IRQ_WAKE_THREAD;
+	} else if (stat & CEC_STAT_ERROR_INIT) {
+		cec->tx_status = CEC_TX_STATUS_ERROR;
+		cec->tx_done = true;
+		ret = IRQ_WAKE_THREAD;
 	}
 
 	if (stat & CEC_STAT_EOM) {
@@ -173,6 +185,8 @@ static irqreturn_t dw_hdmi_cec_thread(in
 
 	if (cec->tx_done) {
 		cec->tx_done = false;
+		if (cec->tx_status == CEC_TX_STATUS_LOW_DRIVE)
+			msleep(100);
 		cec_transmit_attempt_done(adap, cec->tx_status);
 	}
 	if (cec->rx_done) {
