diff mbox

[07/24] drm/bridge/sii8620: add support for burst eMSC transmissions

Message ID 1484897930-1275-8-git-send-email-a.hajda@samsung.com (mailing list archive)
State New, archived
Headers show

Commit Message

Andrzej Hajda Jan. 20, 2017, 7:38 a.m. UTC
Burst transmissions are used in MHL3 mode negotiation.

Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
---
 drivers/gpu/drm/bridge/sil-sii8620.c | 196 ++++++++++++++++++++++++++++++++++-
 drivers/gpu/drm/bridge/sil-sii8620.h |   4 +
 2 files changed, 198 insertions(+), 2 deletions(-)

Comments

Archit Taneja Jan. 23, 2017, 8:20 a.m. UTC | #1
On 01/20/2017 01:08 PM, Andrzej Hajda wrote:
> Burst transmissions are used in MHL3 mode negotiation.
>
> Signed-off-by: Andrzej Hajda <a.hajda@samsung.com>
> ---
>  drivers/gpu/drm/bridge/sil-sii8620.c | 196 ++++++++++++++++++++++++++++++++++-
>  drivers/gpu/drm/bridge/sil-sii8620.h |   4 +
>  2 files changed, 198 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
> index 9f9fd99..744e685 100644
> --- a/drivers/gpu/drm/bridge/sil-sii8620.c
> +++ b/drivers/gpu/drm/bridge/sil-sii8620.c
> @@ -9,6 +9,8 @@
>   * published by the Free Software Foundation.
>   */
>
> +#include <asm/unaligned.h>
> +
>  #include <drm/bridge/mhl.h>
>  #include <drm/drm_crtc.h>
>  #include <drm/drm_edid.h>
> @@ -28,7 +30,8 @@
>
>  #include "sil-sii8620.h"
>
> -#define VAL_RX_HDMI_CTRL2_DEFVAL	VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
> +#define SII8620_BURST_BUF_LEN 288
> +#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
>
>  enum sii8620_mode {
>  	CM_DISCONNECTED,
> @@ -71,6 +74,15 @@ struct sii8620 {
>  	unsigned int gen2_write_burst:1;
>  	enum sii8620_mt_state mt_state;
>  	struct list_head mt_queue;
> +	struct {
> +		int r_size;
> +		int r_count;
> +		int rx_ack;
> +		int rx_count;
> +		u8 rx_buf[32];
> +		int tx_count;
> +		u8 tx_buf[32];
> +	} burst;
>  };
>
>  struct sii8620_mt_msg;
> @@ -511,6 +523,134 @@ static inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg)
>  	sii8620_mt_read_devcap_reg(ctx, reg | 0x80);
>  }
>
> +static void *sii8620_burst_get_tx_buf(struct sii8620 *ctx, int len)
> +{
> +	u8 *buf = &ctx->burst.tx_buf[ctx->burst.tx_count];
> +	int size = len + 2;
> +
> +	if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) {
> +		dev_err(ctx->dev, "TX-BLK buffer exhausted\n");
> +		ctx->error = -EINVAL;
> +		return NULL;
> +	}
> +
> +	ctx->burst.tx_count += size;
> +	buf[1] = len;
> +
> +	return buf + 2;
> +}
> +
> +static u8 *sii8620_burst_get_rx_buf(struct sii8620 *ctx, int len)
> +{
> +	u8 *buf = &ctx->burst.rx_buf[ctx->burst.rx_count];
> +	int size = len + 1;
> +
> +	if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) {
> +		dev_err(ctx->dev, "RX-BLK buffer exhausted\n");
> +		ctx->error = -EINVAL;
> +		return NULL;
> +	}
> +
> +	ctx->burst.rx_count += size;
> +	buf[0] = len;
> +
> +	return buf + 1;
> +}
> +
> +static void sii8620_burst_send(struct sii8620 *ctx)
> +{
> +	int tx_left = ctx->burst.tx_count;
> +	u8 *d = ctx->burst.tx_buf;
> +
> +	while (tx_left > 0) {
> +		int len = d[1] + 2;
> +
> +		if (ctx->burst.r_count + len > ctx->burst.r_size)
> +			break;
> +		d[0] = min(ctx->burst.rx_ack, 255);
> +		ctx->burst.rx_ack -= d[0];
> +		sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, d, len);
> +		ctx->burst.r_count += len;
> +		tx_left -= len;
> +		d += len;
> +	}
> +
> +	ctx->burst.tx_count = tx_left;
> +
> +	while (ctx->burst.rx_ack > 0) {
> +		u8 b[2] = { min(ctx->burst.rx_ack, 255), 0 };
> +
> +		if (ctx->burst.r_count + 2 > ctx->burst.r_size)
> +			break;
> +		ctx->burst.rx_ack -= b[0];
> +		sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, b, 2);
> +		ctx->burst.r_count += 2;
> +	}
> +}
> +
> +static void sii8620_burst_receive(struct sii8620 *ctx)
> +{
> +	u8 buf[3], *d;
> +	int count;
> +
> +	sii8620_read_buf(ctx, REG_EMSCRFIFOBCNTL, buf, 2);
> +	count = get_unaligned_le16(buf);
> +	while (count > 0) {
> +		int len = min(count, 3);
> +
> +		sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, buf, len);
> +		count -= len;
> +		ctx->burst.rx_ack += len - 1;
> +		ctx->burst.r_count -= buf[1];
> +		if (ctx->burst.r_count < 0)
> +			ctx->burst.r_count = 0;
> +
> +		if (len < 3 || !buf[2])
> +			continue;
> +
> +		len = buf[2];
> +		d = sii8620_burst_get_rx_buf(ctx, len);
> +		if (!d)
> +			continue;
> +		sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, d, len);
> +		count -= len;
> +		ctx->burst.rx_ack += len;
> +	}
> +}
> +
> +static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size)
> +{
> +	struct mhl_burst_blk_rcv_buffer_info *d =
> +		sii8620_burst_get_tx_buf(ctx, sizeof(*d));
> +	if (!d)
> +		return;
> +
> +	d->id = cpu_to_be16(MHL_BURST_ID_BLK_RCV_BUFFER_INFO);
> +	d->size = cpu_to_le16(size);
> +}
> +
> +static void sii8620_burst_rx_all(struct sii8620 *ctx)
> +{
> +	u8 *d = ctx->burst.rx_buf;
> +	int count = ctx->burst.rx_count;
> +
> +	while (count-- > 0) {
> +		int len = *d++;
> +		int id = get_unaligned_be16(&d[0]);
> +
> +		switch (id) {
> +		case MHL_BURST_ID_BLK_RCV_BUFFER_INFO:
> +			ctx->burst.r_size = get_unaligned_le16(&d[2]);
> +			break;
> +		default:
> +			break;
> +		}
> +		count -= len;
> +		d += len;
> +	}
> +	ctx->burst.rx_count = 0;
> +}
> +
>  static void sii8620_fetch_edid(struct sii8620 *ctx)
>  {
>  	u8 lm_ddc, ddc_cmd, int3, cbus;
> @@ -1041,7 +1181,7 @@ static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state)
>
>  		if ((s & MSK_COC_STAT_0_FSM_STATE) == state)
>  			return 0;
> -		if (s & BIT_COC_STAT_0_PLL_LOCKED)
> +		if (!(s & BIT_COC_STAT_0_PLL_LOCKED))

Should this be a part of patch #5?

>  			return -EBUSY;
>  		usleep_range(4000, 6000);
>  	}
> @@ -1417,6 +1557,19 @@ static void sii8620_irq_coc(struct sii8620 *ctx)
>  {
>  	u8 stat = sii8620_readb(ctx, REG_COC_INTR);
>
> +	if (stat & BIT_COC_CALIBRATION_DONE) {
> +		u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0);
> +
> +		cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE;
> +		if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) {
> +			sii8620_write_seq_static(ctx,
> +				REG_COC_CTLB, 0,
> +				REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA
> +					      | BIT_TDM_INTR_SYNC_WAIT
> +			);
> +		}
> +	}
> +
>  	sii8620_write(ctx, REG_COC_INTR, stat);
>  }
>
> @@ -1507,6 +1660,41 @@ static void sii8620_irq_infr(struct sii8620 *ctx)
>  		sii8620_start_video(ctx);
>  }
>
> +static void sii8620_irq_tdm(struct sii8620 *ctx)
> +{
> +	u8 stat = sii8620_readb(ctx, REG_TRXINTH);
> +	u8 tdm = sii8620_readb(ctx, REG_TRXSTA2);
> +
> +	if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) {
> +		ctx->mode = CM_ECBUS_S;
> +		ctx->burst.rx_ack = 0;
> +		ctx->burst.r_size = SII8620_BURST_BUF_LEN;
> +		sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN);
> +		sii8620_mt_read_devcap(ctx, true);
> +	} else {
> +		sii8620_write_seq_static(ctx,
> +			REG_MHL_PLL_CTL2, 0,
> +			REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN
> +		);
> +	}
> +
> +	sii8620_write(ctx, REG_TRXINTH, stat);
> +}
> +
> +static void sii8620_irq_block(struct sii8620 *ctx)
> +{
> +	u8 stat = sii8620_readb(ctx, REG_EMSCINTR);
> +
> +	if (stat & BIT_EMSCINTR_SPI_DVLD) {
> +		u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT);
> +
> +		if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE)
> +			sii8620_burst_receive(ctx);
> +	}
> +
> +	sii8620_write(ctx, REG_EMSCINTR, stat);
> +}
> +
>  /* endian agnostic, non-volatile version of test_bit */
>  static bool sii8620_test_bit(unsigned int nr, const u8 *addr)
>  {
> @@ -1522,8 +1710,10 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data)
>  		{ BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc },
>  		{ BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb },
>  		{ BIT_FAST_INTR_STAT_COC, sii8620_irq_coc },
> +		{ BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm },
>  		{ BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc },
>  		{ BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr },
> +		{ BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block },
>  		{ BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid },
>  		{ BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt },
>  		{ BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr },
> @@ -1539,7 +1729,9 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data)
>  		if (sii8620_test_bit(irq_vec[i].bit, stats))
>  			irq_vec[i].handler(ctx);
>
> +	sii8620_burst_rx_all(ctx);
>  	sii8620_mt_work(ctx);
> +	sii8620_burst_send(ctx);
>
>  	ret = sii8620_clear_error(ctx);
>  	if (ret) {
> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h
> index 3ee4e7e..f7bfbc3 100644
> --- a/drivers/gpu/drm/bridge/sil-sii8620.h
> +++ b/drivers/gpu/drm/bridge/sil-sii8620.h
> @@ -403,12 +403,16 @@
>
>  /* TDM RX Status 2nd, default value: 0x00 */
>  #define REG_TRXSTA2				0x015c
> +#define MSK_TDM_SYNCHRONIZED			0xC0

Would be nice to not have the hex digits in caps here.

Thanks,
Archit

> +#define VAL_TDM_SYNCHRONIZED			0x80
>
>  /* TDM RX INT Low, default value: 0x00 */
>  #define REG_TRXINTL				0x0163
>
>  /* TDM RX INT High, default value: 0x00 */
>  #define REG_TRXINTH				0x0164
> +#define BIT_TDM_INTR_SYNC_DATA			BIT(0)
> +#define BIT_TDM_INTR_SYNC_WAIT			BIT(1)
>
>  /* TDM RX INTMASK High, default value: 0x00 */
>  #define REG_TRXINTMH				0x0166
>
Andrzej Hajda Jan. 23, 2017, 10:38 a.m. UTC | #2
On 23.01.2017 09:20, Archit Taneja wrote:
(...)
>>  static void sii8620_fetch_edid(struct sii8620 *ctx)
>>  {
>>  	u8 lm_ddc, ddc_cmd, int3, cbus;
>> @@ -1041,7 +1181,7 @@ static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state)
>>
>>  		if ((s & MSK_COC_STAT_0_FSM_STATE) == state)
>>  			return 0;
>> -		if (s & BIT_COC_STAT_0_PLL_LOCKED)
>> +		if (!(s & BIT_COC_STAT_0_PLL_LOCKED))
> Should this be a part of patch #5?

Yes, of course, it is just fix for patch #5.

>
>>  			return -EBUSY;
>>  		usleep_range(4000, 6000);
>>  	}
>> @@ -1417,6 +1557,19 @@ static void sii8620_irq_coc(struct sii8620 *ctx)
>>  {
>>  	u8 stat = sii8620_readb(ctx, REG_COC_INTR);
>>
>> +	if (stat & BIT_COC_CALIBRATION_DONE) {
>> +		u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0);
>> +
>> +		cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE;
>> +		if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) {
>> +			sii8620_write_seq_static(ctx,
>> +				REG_COC_CTLB, 0,
>> +				REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA
>> +					      | BIT_TDM_INTR_SYNC_WAIT
>> +			);
>> +		}
>> +	}
>> +
>>  	sii8620_write(ctx, REG_COC_INTR, stat);
>>  }
>>
>> @@ -1507,6 +1660,41 @@ static void sii8620_irq_infr(struct sii8620 *ctx)
>>  		sii8620_start_video(ctx);
>>  }
>>
>> +static void sii8620_irq_tdm(struct sii8620 *ctx)
>> +{
>> +	u8 stat = sii8620_readb(ctx, REG_TRXINTH);
>> +	u8 tdm = sii8620_readb(ctx, REG_TRXSTA2);
>> +
>> +	if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) {
>> +		ctx->mode = CM_ECBUS_S;
>> +		ctx->burst.rx_ack = 0;
>> +		ctx->burst.r_size = SII8620_BURST_BUF_LEN;
>> +		sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN);
>> +		sii8620_mt_read_devcap(ctx, true);
>> +	} else {
>> +		sii8620_write_seq_static(ctx,
>> +			REG_MHL_PLL_CTL2, 0,
>> +			REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN
>> +		);
>> +	}
>> +
>> +	sii8620_write(ctx, REG_TRXINTH, stat);
>> +}
>> +
>> +static void sii8620_irq_block(struct sii8620 *ctx)
>> +{
>> +	u8 stat = sii8620_readb(ctx, REG_EMSCINTR);
>> +
>> +	if (stat & BIT_EMSCINTR_SPI_DVLD) {
>> +		u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT);
>> +
>> +		if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE)
>> +			sii8620_burst_receive(ctx);
>> +	}
>> +
>> +	sii8620_write(ctx, REG_EMSCINTR, stat);
>> +}
>> +
>>  /* endian agnostic, non-volatile version of test_bit */
>>  static bool sii8620_test_bit(unsigned int nr, const u8 *addr)
>>  {
>> @@ -1522,8 +1710,10 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data)
>>  		{ BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc },
>>  		{ BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb },
>>  		{ BIT_FAST_INTR_STAT_COC, sii8620_irq_coc },
>> +		{ BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm },
>>  		{ BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc },
>>  		{ BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr },
>> +		{ BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block },
>>  		{ BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid },
>>  		{ BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt },
>>  		{ BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr },
>> @@ -1539,7 +1729,9 @@ static irqreturn_t sii8620_irq_thread(int irq, void *data)
>>  		if (sii8620_test_bit(irq_vec[i].bit, stats))
>>  			irq_vec[i].handler(ctx);
>>
>> +	sii8620_burst_rx_all(ctx);
>>  	sii8620_mt_work(ctx);
>> +	sii8620_burst_send(ctx);
>>
>>  	ret = sii8620_clear_error(ctx);
>>  	if (ret) {
>> diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h
>> index 3ee4e7e..f7bfbc3 100644
>> --- a/drivers/gpu/drm/bridge/sil-sii8620.h
>> +++ b/drivers/gpu/drm/bridge/sil-sii8620.h
>> @@ -403,12 +403,16 @@
>>
>>  /* TDM RX Status 2nd, default value: 0x00 */
>>  #define REG_TRXSTA2				0x015c
>> +#define MSK_TDM_SYNCHRONIZED			0xC0
> Would be nice to not have the hex digits in caps here.


OK, this one somehow sneaked out :)

Regards
Andrzej

>
> Thanks,
> Archit
>
>> +#define VAL_TDM_SYNCHRONIZED			0x80
>>
>>  /* TDM RX INT Low, default value: 0x00 */
>>  #define REG_TRXINTL				0x0163
>>
>>  /* TDM RX INT High, default value: 0x00 */
>>  #define REG_TRXINTH				0x0164
>> +#define BIT_TDM_INTR_SYNC_DATA			BIT(0)
>> +#define BIT_TDM_INTR_SYNC_WAIT			BIT(1)
>>
>>  /* TDM RX INTMASK High, default value: 0x00 */
>>  #define REG_TRXINTMH				0x0166
>>
diff mbox

Patch

diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index 9f9fd99..744e685 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -9,6 +9,8 @@ 
  * published by the Free Software Foundation.
  */
 
+#include <asm/unaligned.h>
+
 #include <drm/bridge/mhl.h>
 #include <drm/drm_crtc.h>
 #include <drm/drm_edid.h>
@@ -28,7 +30,8 @@ 
 
 #include "sil-sii8620.h"
 
-#define VAL_RX_HDMI_CTRL2_DEFVAL	VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
+#define SII8620_BURST_BUF_LEN 288
+#define VAL_RX_HDMI_CTRL2_DEFVAL VAL_RX_HDMI_CTRL2_IDLE_CNT(3)
 
 enum sii8620_mode {
 	CM_DISCONNECTED,
@@ -71,6 +74,15 @@  struct sii8620 {
 	unsigned int gen2_write_burst:1;
 	enum sii8620_mt_state mt_state;
 	struct list_head mt_queue;
+	struct {
+		int r_size;
+		int r_count;
+		int rx_ack;
+		int rx_count;
+		u8 rx_buf[32];
+		int tx_count;
+		u8 tx_buf[32];
+	} burst;
 };
 
 struct sii8620_mt_msg;
@@ -511,6 +523,134 @@  static inline void sii8620_mt_read_xdevcap_reg(struct sii8620 *ctx, u8 reg)
 	sii8620_mt_read_devcap_reg(ctx, reg | 0x80);
 }
 
+static void *sii8620_burst_get_tx_buf(struct sii8620 *ctx, int len)
+{
+	u8 *buf = &ctx->burst.tx_buf[ctx->burst.tx_count];
+	int size = len + 2;
+
+	if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) {
+		dev_err(ctx->dev, "TX-BLK buffer exhausted\n");
+		ctx->error = -EINVAL;
+		return NULL;
+	}
+
+	ctx->burst.tx_count += size;
+	buf[1] = len;
+
+	return buf + 2;
+}
+
+static u8 *sii8620_burst_get_rx_buf(struct sii8620 *ctx, int len)
+{
+	u8 *buf = &ctx->burst.rx_buf[ctx->burst.rx_count];
+	int size = len + 1;
+
+	if (ctx->burst.tx_count + size > ARRAY_SIZE(ctx->burst.tx_buf)) {
+		dev_err(ctx->dev, "RX-BLK buffer exhausted\n");
+		ctx->error = -EINVAL;
+		return NULL;
+	}
+
+	ctx->burst.rx_count += size;
+	buf[0] = len;
+
+	return buf + 1;
+}
+
+static void sii8620_burst_send(struct sii8620 *ctx)
+{
+	int tx_left = ctx->burst.tx_count;
+	u8 *d = ctx->burst.tx_buf;
+
+	while (tx_left > 0) {
+		int len = d[1] + 2;
+
+		if (ctx->burst.r_count + len > ctx->burst.r_size)
+			break;
+		d[0] = min(ctx->burst.rx_ack, 255);
+		ctx->burst.rx_ack -= d[0];
+		sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, d, len);
+		ctx->burst.r_count += len;
+		tx_left -= len;
+		d += len;
+	}
+
+	ctx->burst.tx_count = tx_left;
+
+	while (ctx->burst.rx_ack > 0) {
+		u8 b[2] = { min(ctx->burst.rx_ack, 255), 0 };
+
+		if (ctx->burst.r_count + 2 > ctx->burst.r_size)
+			break;
+		ctx->burst.rx_ack -= b[0];
+		sii8620_write_buf(ctx, REG_EMSC_XMIT_WRITE_PORT, b, 2);
+		ctx->burst.r_count += 2;
+	}
+}
+
+static void sii8620_burst_receive(struct sii8620 *ctx)
+{
+	u8 buf[3], *d;
+	int count;
+
+	sii8620_read_buf(ctx, REG_EMSCRFIFOBCNTL, buf, 2);
+	count = get_unaligned_le16(buf);
+	while (count > 0) {
+		int len = min(count, 3);
+
+		sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, buf, len);
+		count -= len;
+		ctx->burst.rx_ack += len - 1;
+		ctx->burst.r_count -= buf[1];
+		if (ctx->burst.r_count < 0)
+			ctx->burst.r_count = 0;
+
+		if (len < 3 || !buf[2])
+			continue;
+
+		len = buf[2];
+		d = sii8620_burst_get_rx_buf(ctx, len);
+		if (!d)
+			continue;
+		sii8620_read_buf(ctx, REG_EMSC_RCV_READ_PORT, d, len);
+		count -= len;
+		ctx->burst.rx_ack += len;
+	}
+}
+
+static void sii8620_burst_tx_rbuf_info(struct sii8620 *ctx, int size)
+{
+	struct mhl_burst_blk_rcv_buffer_info *d =
+		sii8620_burst_get_tx_buf(ctx, sizeof(*d));
+	if (!d)
+		return;
+
+	d->id = cpu_to_be16(MHL_BURST_ID_BLK_RCV_BUFFER_INFO);
+	d->size = cpu_to_le16(size);
+}
+
+static void sii8620_burst_rx_all(struct sii8620 *ctx)
+{
+	u8 *d = ctx->burst.rx_buf;
+	int count = ctx->burst.rx_count;
+
+	while (count-- > 0) {
+		int len = *d++;
+		int id = get_unaligned_be16(&d[0]);
+
+		switch (id) {
+		case MHL_BURST_ID_BLK_RCV_BUFFER_INFO:
+			ctx->burst.r_size = get_unaligned_le16(&d[2]);
+			break;
+		default:
+			break;
+		}
+		count -= len;
+		d += len;
+	}
+	ctx->burst.rx_count = 0;
+}
+
 static void sii8620_fetch_edid(struct sii8620 *ctx)
 {
 	u8 lm_ddc, ddc_cmd, int3, cbus;
@@ -1041,7 +1181,7 @@  static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state)
 
 		if ((s & MSK_COC_STAT_0_FSM_STATE) == state)
 			return 0;
-		if (s & BIT_COC_STAT_0_PLL_LOCKED)
+		if (!(s & BIT_COC_STAT_0_PLL_LOCKED))
 			return -EBUSY;
 		usleep_range(4000, 6000);
 	}
@@ -1417,6 +1557,19 @@  static void sii8620_irq_coc(struct sii8620 *ctx)
 {
 	u8 stat = sii8620_readb(ctx, REG_COC_INTR);
 
+	if (stat & BIT_COC_CALIBRATION_DONE) {
+		u8 cstat = sii8620_readb(ctx, REG_COC_STAT_0);
+
+		cstat &= BIT_COC_STAT_0_PLL_LOCKED | MSK_COC_STAT_0_FSM_STATE;
+		if (cstat == (BIT_COC_STAT_0_PLL_LOCKED | 0x02)) {
+			sii8620_write_seq_static(ctx,
+				REG_COC_CTLB, 0,
+				REG_TRXINTMH, BIT_TDM_INTR_SYNC_DATA
+					      | BIT_TDM_INTR_SYNC_WAIT
+			);
+		}
+	}
+
 	sii8620_write(ctx, REG_COC_INTR, stat);
 }
 
@@ -1507,6 +1660,41 @@  static void sii8620_irq_infr(struct sii8620 *ctx)
 		sii8620_start_video(ctx);
 }
 
+static void sii8620_irq_tdm(struct sii8620 *ctx)
+{
+	u8 stat = sii8620_readb(ctx, REG_TRXINTH);
+	u8 tdm = sii8620_readb(ctx, REG_TRXSTA2);
+
+	if ((tdm & MSK_TDM_SYNCHRONIZED) == VAL_TDM_SYNCHRONIZED) {
+		ctx->mode = CM_ECBUS_S;
+		ctx->burst.rx_ack = 0;
+		ctx->burst.r_size = SII8620_BURST_BUF_LEN;
+		sii8620_burst_tx_rbuf_info(ctx, SII8620_BURST_BUF_LEN);
+		sii8620_mt_read_devcap(ctx, true);
+	} else {
+		sii8620_write_seq_static(ctx,
+			REG_MHL_PLL_CTL2, 0,
+			REG_MHL_PLL_CTL2, BIT_MHL_PLL_CTL2_CLKDETECT_EN
+		);
+	}
+
+	sii8620_write(ctx, REG_TRXINTH, stat);
+}
+
+static void sii8620_irq_block(struct sii8620 *ctx)
+{
+	u8 stat = sii8620_readb(ctx, REG_EMSCINTR);
+
+	if (stat & BIT_EMSCINTR_SPI_DVLD) {
+		u8 bstat = sii8620_readb(ctx, REG_SPIBURSTSTAT);
+
+		if (bstat & BIT_SPIBURSTSTAT_EMSC_NORMAL_MODE)
+			sii8620_burst_receive(ctx);
+	}
+
+	sii8620_write(ctx, REG_EMSCINTR, stat);
+}
+
 /* endian agnostic, non-volatile version of test_bit */
 static bool sii8620_test_bit(unsigned int nr, const u8 *addr)
 {
@@ -1522,8 +1710,10 @@  static irqreturn_t sii8620_irq_thread(int irq, void *data)
 		{ BIT_FAST_INTR_STAT_DISC, sii8620_irq_disc },
 		{ BIT_FAST_INTR_STAT_G2WB, sii8620_irq_g2wb },
 		{ BIT_FAST_INTR_STAT_COC, sii8620_irq_coc },
+		{ BIT_FAST_INTR_STAT_TDM, sii8620_irq_tdm },
 		{ BIT_FAST_INTR_STAT_MSC, sii8620_irq_msc },
 		{ BIT_FAST_INTR_STAT_MERR, sii8620_irq_merr },
+		{ BIT_FAST_INTR_STAT_BLOCK, sii8620_irq_block },
 		{ BIT_FAST_INTR_STAT_EDID, sii8620_irq_edid },
 		{ BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt },
 		{ BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr },
@@ -1539,7 +1729,9 @@  static irqreturn_t sii8620_irq_thread(int irq, void *data)
 		if (sii8620_test_bit(irq_vec[i].bit, stats))
 			irq_vec[i].handler(ctx);
 
+	sii8620_burst_rx_all(ctx);
 	sii8620_mt_work(ctx);
+	sii8620_burst_send(ctx);
 
 	ret = sii8620_clear_error(ctx);
 	if (ret) {
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.h b/drivers/gpu/drm/bridge/sil-sii8620.h
index 3ee4e7e..f7bfbc3 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.h
+++ b/drivers/gpu/drm/bridge/sil-sii8620.h
@@ -403,12 +403,16 @@ 
 
 /* TDM RX Status 2nd, default value: 0x00 */
 #define REG_TRXSTA2				0x015c
+#define MSK_TDM_SYNCHRONIZED			0xC0
+#define VAL_TDM_SYNCHRONIZED			0x80
 
 /* TDM RX INT Low, default value: 0x00 */
 #define REG_TRXINTL				0x0163
 
 /* TDM RX INT High, default value: 0x00 */
 #define REG_TRXINTH				0x0164
+#define BIT_TDM_INTR_SYNC_DATA			BIT(0)
+#define BIT_TDM_INTR_SYNC_WAIT			BIT(1)
 
 /* TDM RX INTMASK High, default value: 0x00 */
 #define REG_TRXINTMH				0x0166