diff options
Diffstat (limited to 'drivers/gpu/drm/bridge/sil-sii8620.c')
-rw-r--r-- | drivers/gpu/drm/bridge/sil-sii8620.c | 949 |
1 files changed, 815 insertions, 134 deletions
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c index b2c267df7ee7..cdd0a9d44ba1 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,10 @@ #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) +#define MHL1_MAX_LCLK 225000 +#define MHL3_MAX_LCLK 600000 enum sii8620_mode { CM_DISCONNECTED, @@ -59,6 +64,9 @@ struct sii8620 { struct regulator_bulk_data supplies[2]; struct mutex lock; /* context lock, protects fields below */ int error; + int pixel_clock; + unsigned int use_packed_pixel:1; + int video_code; enum sii8620_mode mode; enum sii8620_sink_type sink_type; u8 cbus_status; @@ -66,11 +74,20 @@ struct sii8620 { u8 xstat[MHL_XDS_SIZE]; u8 devcap[MHL_DCAP_SIZE]; u8 xdevcap[MHL_XDC_SIZE]; - u8 avif[19]; + u8 avif[HDMI_INFOFRAME_SIZE(AVI)]; struct edid *edid; 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; @@ -78,12 +95,15 @@ struct sii8620_mt_msg; typedef void (*sii8620_mt_msg_cb)(struct sii8620 *ctx, struct sii8620_mt_msg *msg); +typedef void (*sii8620_cb)(struct sii8620 *ctx, int ret); + struct sii8620_mt_msg { struct list_head node; u8 reg[4]; u8 ret; sii8620_mt_msg_cb send; sii8620_mt_msg_cb recv; + sii8620_cb continuation; }; static const u8 sii8620_i2c_page[] = { @@ -101,6 +121,7 @@ static void sii8620_fetch_edid(struct sii8620 *ctx); static void sii8620_set_upstream_edid(struct sii8620 *ctx); static void sii8620_enable_hpd(struct sii8620 *ctx); static void sii8620_mhl_disconnected(struct sii8620 *ctx); +static void sii8620_disconnect(struct sii8620 *ctx); static int sii8620_clear_error(struct sii8620 *ctx) { @@ -227,6 +248,11 @@ static void sii8620_setbits(struct sii8620 *ctx, u16 addr, u8 mask, u8 val) sii8620_write(ctx, addr, val); } +static inline bool sii8620_is_mhl3(struct sii8620 *ctx) +{ + return ctx->mode >= CM_MHL3; +} + static void sii8620_mt_cleanup(struct sii8620 *ctx) { struct sii8620_mt_msg *msg, *n; @@ -251,9 +277,11 @@ static void sii8620_mt_work(struct sii8620 *ctx) ctx->mt_state = MT_STATE_READY; msg = list_first_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + list_del(&msg->node); if (msg->recv) msg->recv(ctx, msg); - list_del(&msg->node); + if (msg->continuation) + msg->continuation(ctx, msg->ret); kfree(msg); } @@ -266,9 +294,59 @@ static void sii8620_mt_work(struct sii8620 *ctx) msg->send(ctx, msg); } +static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) +{ + u8 ctrl = BIT_MDT_RCV_CTRL_MDT_RCV_EN; + + if (ctx->gen2_write_burst) + return; + + if (ctx->mode >= CM_MHL1) + ctrl |= BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN; + + sii8620_write_seq(ctx, + REG_MDT_RCV_TIMEOUT, 100, + REG_MDT_RCV_CTRL, ctrl + ); + ctx->gen2_write_burst = 1; +} + +static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) +{ + if (!ctx->gen2_write_burst) + return; + + sii8620_write_seq_static(ctx, + REG_MDT_XMIT_CTRL, 0, + REG_MDT_RCV_CTRL, 0 + ); + ctx->gen2_write_burst = 0; +} + +static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) +{ + sii8620_write_seq_static(ctx, + REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT + | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR + | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD + | BIT_MDT_XMIT_SM_ERROR, + REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY + | BIT_MDT_IDLE_AFTER_HAWB_DISABLE + | BIT_MDT_RFIFO_DATA_RDY + ); + sii8620_enable_gen2_write_burst(ctx); +} + static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, struct sii8620_mt_msg *msg) { + if (msg->reg[0] == MHL_SET_INT && + msg->reg[1] == MHL_INT_REG(RCHANGE) && + msg->reg[2] == MHL_INT_RC_FEAT_REQ) + sii8620_enable_gen2_write_burst(ctx); + else + sii8620_disable_gen2_write_burst(ctx); + switch (msg->reg[0]) { case MHL_WRITE_STAT: case MHL_SET_INT: @@ -281,6 +359,12 @@ static void sii8620_mt_msc_cmd_send(struct sii8620 *ctx, sii8620_write(ctx, REG_MSC_COMMAND_START, BIT_MSC_COMMAND_START_MSC_MSG); break; + case MHL_READ_DEVCAP_REG: + case MHL_READ_XDEVCAP_REG: + sii8620_write(ctx, REG_MSC_CMD_OR_OFFSET, msg->reg[1]); + sii8620_write(ctx, REG_MSC_COMMAND_START, + BIT_MSC_COMMAND_START_READ_DEVCAP); + break; default: dev_err(ctx->dev, "%s: command %#x not supported\n", __func__, msg->reg[0]); @@ -299,6 +383,21 @@ static struct sii8620_mt_msg *sii8620_mt_msg_new(struct sii8620 *ctx) return msg; } +static void sii8620_mt_set_cont(struct sii8620 *ctx, sii8620_cb cont) +{ + struct sii8620_mt_msg *msg; + + if (ctx->error) + return; + + if (list_empty(&ctx->mt_queue)) { + ctx->error = -EINVAL; + return; + } + msg = list_last_entry(&ctx->mt_queue, struct sii8620_mt_msg, node); + msg->continuation = cont; +} + static void sii8620_mt_msc_cmd(struct sii8620 *ctx, u8 cmd, u8 arg1, u8 arg2) { struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); @@ -358,7 +457,7 @@ static void sii8620_update_array(u8 *dst, u8 *src, int count) } } -static void sii8620_mr_devcap(struct sii8620 *ctx) +static void sii8620_sink_detected(struct sii8620 *ctx, int ret) { static const char * const sink_str[] = { [SINK_NONE] = "NONE", @@ -366,23 +465,10 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) [SINK_DVI] = "DVI" }; - u8 dcap[MHL_DCAP_SIZE]; char sink_name[20]; struct device *dev = ctx->dev; - sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); - if (ctx->error < 0) - return; - - dev_info(dev, "dcap: %*ph\n", MHL_DCAP_SIZE, dcap); - dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", - dcap[MHL_DCAP_MHL_VERSION] / 16, - dcap[MHL_DCAP_MHL_VERSION] % 16, dcap[MHL_DCAP_ADOPTER_ID_H], - dcap[MHL_DCAP_ADOPTER_ID_L], dcap[MHL_DCAP_DEVICE_ID_H], - dcap[MHL_DCAP_DEVICE_ID_L]); - sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); - - if (!(dcap[MHL_DCAP_CAT] & MHL_DCAP_CAT_SINK)) + if (ret < 0) return; sii8620_fetch_edid(ctx); @@ -401,18 +487,76 @@ static void sii8620_mr_devcap(struct sii8620 *ctx) dev_info(dev, "detected sink(type: %s): %s\n", sink_str[ctx->sink_type], sink_name); +} + +static void sii8620_hsic_init(struct sii8620 *ctx) +{ + if (!sii8620_is_mhl3(ctx)) + return; + + sii8620_write(ctx, REG_FCGC, + BIT_FCGC_HSIC_HOSTMODE | BIT_FCGC_HSIC_ENABLE); + sii8620_setbits(ctx, REG_HRXCTRL3, + BIT_HRXCTRL3_HRX_STAY_RESET | BIT_HRXCTRL3_STATUS_EN, ~0); + sii8620_setbits(ctx, REG_TTXNUMB, MSK_TTXNUMB_TTX_NUMBPS, 4); + sii8620_setbits(ctx, REG_TRXCTRL, BIT_TRXCTRL_TRX_FROM_SE_COC, ~0); + sii8620_setbits(ctx, REG_HTXCTRL, BIT_HTXCTRL_HTX_DRVCONN1, 0); + sii8620_setbits(ctx, REG_KEEPER, MSK_KEEPER_MODE, VAL_KEEPER_MODE_HOST); + sii8620_write_seq_static(ctx, + REG_TDMLLCTL, 0, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST | + BIT_UTSRST_KEEPER_SRST | BIT_UTSRST_FC_SRST, + REG_UTSRST, BIT_UTSRST_HRX_SRST | BIT_UTSRST_HTX_SRST, + REG_HRXINTL, 0xff, + REG_HRXINTH, 0xff, + REG_TTXINTL, 0xff, + REG_TTXINTH, 0xff, + REG_TRXINTL, 0xff, + REG_TRXINTH, 0xff, + REG_HTXINTL, 0xff, + REG_HTXINTH, 0xff, + REG_FCINTR0, 0xff, + REG_FCINTR1, 0xff, + REG_FCINTR2, 0xff, + REG_FCINTR3, 0xff, + REG_FCINTR4, 0xff, + REG_FCINTR5, 0xff, + REG_FCINTR6, 0xff, + REG_FCINTR7, 0xff + ); +} + +static void sii8620_edid_read(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + sii8620_set_upstream_edid(ctx); + sii8620_hsic_init(ctx); sii8620_enable_hpd(ctx); } +static void sii8620_mr_devcap(struct sii8620 *ctx) +{ + u8 dcap[MHL_DCAP_SIZE]; + struct device *dev = ctx->dev; + + sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, dcap, MHL_DCAP_SIZE); + if (ctx->error < 0) + return; + + dev_info(dev, "detected dongle MHL %d.%d, ChipID %02x%02x:%02x%02x\n", + dcap[MHL_DCAP_MHL_VERSION] / 16, + dcap[MHL_DCAP_MHL_VERSION] % 16, + dcap[MHL_DCAP_ADOPTER_ID_H], dcap[MHL_DCAP_ADOPTER_ID_L], + dcap[MHL_DCAP_DEVICE_ID_H], dcap[MHL_DCAP_DEVICE_ID_L]); + sii8620_update_array(ctx->devcap, dcap, MHL_DCAP_SIZE); +} + static void sii8620_mr_xdevcap(struct sii8620 *ctx) { sii8620_read_buf(ctx, REG_EDID_FIFO_RD_DATA, ctx->xdevcap, MHL_XDC_SIZE); - - sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), - MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); - sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); } static void sii8620_mt_read_devcap_recv(struct sii8620 *ctx, @@ -450,6 +594,197 @@ static void sii8620_mt_read_devcap(struct sii8620 *ctx, bool xdevcap) msg->recv = sii8620_mt_read_devcap_recv; } +static void sii8620_mt_read_devcap_reg_recv(struct sii8620 *ctx, + struct sii8620_mt_msg *msg) +{ + u8 reg = msg->reg[0] & 0x7f; + + if (msg->reg[0] & 0x80) + ctx->xdevcap[reg] = msg->ret; + else + ctx->devcap[reg] = msg->ret; +} + +static void sii8620_mt_read_devcap_reg(struct sii8620 *ctx, u8 reg) +{ + struct sii8620_mt_msg *msg = sii8620_mt_msg_new(ctx); + + if (!msg) + return; + + msg->reg[0] = (reg & 0x80) ? MHL_READ_XDEVCAP_REG : MHL_READ_DEVCAP_REG; + msg->reg[1] = reg; + msg->send = sii8620_mt_msc_cmd_send; + msg->recv = sii8620_mt_read_devcap_reg_recv; +} + +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 u8 sii8620_checksum(void *ptr, int size) +{ + u8 *d = ptr, sum = 0; + + while (size--) + sum += *d++; + + return sum; +} + +static void sii8620_mhl_burst_hdr_set(struct mhl3_burst_header *h, + enum mhl_burst_id id) +{ + h->id = cpu_to_be16(id); + h->total_entries = 1; + h->sequence_index = 1; +} + +static void sii8620_burst_tx_bits_per_pixel_fmt(struct sii8620 *ctx, u8 fmt) +{ + struct mhl_burst_bits_per_pixel_fmt *d; + const int size = sizeof(*d) + sizeof(d->desc[0]); + + d = sii8620_burst_get_tx_buf(ctx, size); + if (!d) + return; + + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_BITS_PER_PIXEL_FMT); + d->num_entries = 1; + d->desc[0].stream_id = 0; + d->desc[0].pixel_format = fmt; + d->hdr.checksum -= sii8620_checksum(d, 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; @@ -537,12 +872,12 @@ static void sii8620_fetch_edid(struct sii8620 *ctx) edid = new_edid; } } - - if (fetched + FETCH_SIZE == edid_len) - sii8620_write(ctx, REG_INTR3, int3); } - sii8620_write(ctx, REG_LM_DDC, lm_ddc); + sii8620_write_seq(ctx, + REG_INTR3_MASK, BIT_DDC_CMD_DONE, + REG_LM_DDC, lm_ddc + ); end: kfree(ctx->edid); @@ -641,11 +976,10 @@ static void sii8620_hw_reset(struct sii8620 *ctx) static void sii8620_cbus_reset(struct sii8620 *ctx) { - sii8620_write_seq_static(ctx, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST - | BIT_PWD_SRST_CBUS_RST_SW_EN, - REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN - ); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN); + usleep_range(10000, 20000); + sii8620_write(ctx, REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN); } static void sii8620_set_auto_zone(struct sii8620 *ctx) @@ -683,48 +1017,208 @@ static void sii8620_stop_video(struct sii8620 *ctx) | BIT_TPI_SC_TPI_AV_MUTE; break; case SINK_HDMI: + default: val = BIT_TPI_SC_REG_TMDS_OE_POWER_DOWN | BIT_TPI_SC_TPI_AV_MUTE | BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI; break; - default: - return; } sii8620_write(ctx, REG_TPI_SC, val); } +static void sii8620_set_format(struct sii8620 *ctx) +{ + u8 out_fmt; + + if (sii8620_is_mhl3(ctx)) { + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PIXEL_MODE_PACKED, + ctx->use_packed_pixel ? ~0 : 0); + } else { + if (ctx->use_packed_pixel) + sii8620_write_seq_static(ctx, + REG_VID_MODE, BIT_VID_MODE_M1080P, + REG_MHL_TOP_CTL, BIT_MHL_TOP_CTL_MHL_PP_SEL | 1, + REG_MHLTX_CTL6, 0x60 + ); + else + sii8620_write_seq_static(ctx, + REG_VID_MODE, 0, + REG_MHL_TOP_CTL, 1, + REG_MHLTX_CTL6, 0xa0 + ); + } + + if (ctx->use_packed_pixel) + out_fmt = VAL_TPI_FORMAT(YCBCR422, FULL) | + BIT_TPI_OUTPUT_CSCMODE709; + else + out_fmt = VAL_TPI_FORMAT(RGB, FULL); + + sii8620_write_seq(ctx, + REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), + REG_TPI_OUTPUT, out_fmt, + ); +} + +static int mhl3_infoframe_init(struct mhl3_infoframe *frame) +{ + memset(frame, 0, sizeof(*frame)); + + frame->version = 3; + frame->hev_format = -1; + return 0; +} + +static ssize_t mhl3_infoframe_pack(struct mhl3_infoframe *frame, + void *buffer, size_t size) +{ + const int frm_len = HDMI_INFOFRAME_HEADER_SIZE + MHL3_INFOFRAME_SIZE; + u8 *ptr = buffer; + + if (size < frm_len) + return -ENOSPC; + + memset(buffer, 0, size); + ptr[0] = HDMI_INFOFRAME_TYPE_VENDOR; + ptr[1] = frame->version; + ptr[2] = MHL3_INFOFRAME_SIZE; + ptr[4] = MHL3_IEEE_OUI & 0xff; + ptr[5] = (MHL3_IEEE_OUI >> 8) & 0xff; + ptr[6] = (MHL3_IEEE_OUI >> 16) & 0xff; + ptr[7] = frame->video_format & 0x3; + ptr[7] |= (frame->format_type & 0x7) << 2; + ptr[7] |= frame->sep_audio ? BIT(5) : 0; + if (frame->hev_format >= 0) { + ptr[9] = 1; + ptr[10] = (frame->hev_format >> 8) & 0xff; + ptr[11] = frame->hev_format & 0xff; + } + if (frame->av_delay) { + bool sign = frame->av_delay < 0; + int delay = sign ? -frame->av_delay : frame->av_delay; + + ptr[12] = (delay >> 16) & 0xf; + if (sign) + ptr[12] |= BIT(4); + ptr[13] = (delay >> 8) & 0xff; + ptr[14] = delay & 0xff; + } + ptr[3] -= sii8620_checksum(buffer, frm_len); + return frm_len; +} + +static void sii8620_set_infoframes(struct sii8620 *ctx) +{ + struct mhl3_infoframe mhl_frm; + union hdmi_infoframe frm; + u8 buf[31]; + int ret; + + if (!sii8620_is_mhl3(ctx) || !ctx->use_packed_pixel) { + sii8620_write(ctx, REG_TPI_SC, + BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif + 3, + ARRAY_SIZE(ctx->avif) - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_DROP_GEN_PKT); + return; + } + + ret = hdmi_avi_infoframe_init(&frm.avi); + frm.avi.colorspace = HDMI_COLORSPACE_YUV422; + frm.avi.active_aspect = HDMI_ACTIVE_ASPECT_PICTURE; + frm.avi.picture_aspect = HDMI_PICTURE_ASPECT_16_9; + frm.avi.colorimetry = HDMI_COLORIMETRY_ITU_709; + frm.avi.video_code = ctx->video_code; + if (!ret) + ret = hdmi_avi_infoframe_pack(&frm.avi, buf, ARRAY_SIZE(buf)); + if (ret > 0) + sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, buf + 3, ret - 3); + sii8620_write(ctx, REG_PKT_FILTER_0, + BIT_PKT_FILTER_0_DROP_CEA_GAMUT_PKT | + BIT_PKT_FILTER_0_DROP_MPEG_PKT | + BIT_PKT_FILTER_0_DROP_AVI_PKT | + BIT_PKT_FILTER_0_DROP_GCP_PKT, + BIT_PKT_FILTER_1_VSI_OVERRIDE_DIS | + BIT_PKT_FILTER_1_DROP_GEN_PKT | + BIT_PKT_FILTER_1_DROP_VSIF_PKT); + + sii8620_write(ctx, REG_TPI_INFO_FSEL, BIT_TPI_INFO_FSEL_EN + | BIT_TPI_INFO_FSEL_RPT | VAL_TPI_INFO_FSEL_VSI); + ret = mhl3_infoframe_init(&mhl_frm); + if (!ret) + ret = mhl3_infoframe_pack(&mhl_frm, buf, ARRAY_SIZE(buf)); + sii8620_write_buf(ctx, REG_TPI_INFO_B0, buf, ret); +} + static void sii8620_start_hdmi(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, REG_RX_HDMI_CTRL2, VAL_RX_HDMI_CTRL2_DEFVAL | BIT_RX_HDMI_CTRL2_USE_AV_MUTE, REG_VID_OVRRD, BIT_VID_OVRRD_PP_AUTO_DISABLE - | BIT_VID_OVRRD_M1080P_OVRRD, - REG_VID_MODE, 0, - REG_MHL_TOP_CTL, 0x1, - REG_MHLTX_CTL6, 0xa0, - REG_TPI_INPUT, VAL_TPI_FORMAT(RGB, FULL), - REG_TPI_OUTPUT, VAL_TPI_FORMAT(RGB, FULL), - ); - - sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), - MHL_DST_LM_CLK_MODE_NORMAL | - MHL_DST_LM_PATH_ENABLED); + | BIT_VID_OVRRD_M1080P_OVRRD); + sii8620_set_format(ctx); - sii8620_set_auto_zone(ctx); + if (!sii8620_is_mhl3(ctx)) { + sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), + MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); + sii8620_set_auto_zone(ctx); + } else { + static const struct { + int max_clk; + u8 zone; + u8 link_rate; + u8 rrp_decode; + } clk_spec[] = { + { 150000, VAL_TX_ZONE_CTL3_TX_ZONE_1_5GBPS, + MHL_XDS_LINK_RATE_1_5_GBPS, 0x38 }, + { 300000, VAL_TX_ZONE_CTL3_TX_ZONE_3GBPS, + MHL_XDS_LINK_RATE_3_0_GBPS, 0x40 }, + { 600000, VAL_TX_ZONE_CTL3_TX_ZONE_6GBPS, + MHL_XDS_LINK_RATE_6_0_GBPS, 0x40 }, + }; + u8 p0_ctrl = BIT_M3_P0CTRL_MHL3_P0_PORT_EN; + int clk = ctx->pixel_clock * (ctx->use_packed_pixel ? 2 : 3); + int i; + + for (i = 0; i < ARRAY_SIZE(clk_spec); ++i) + if (clk < clk_spec[i].max_clk) + break; - sii8620_write(ctx, REG_TPI_SC, BIT_TPI_SC_TPI_OUTPUT_MODE_0_HDMI); + if (100 * clk >= 98 * clk_spec[i].max_clk) + p0_ctrl |= BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN; - sii8620_write_buf(ctx, REG_TPI_AVI_CHSUM, ctx->avif, - ARRAY_SIZE(ctx->avif)); + sii8620_burst_tx_bits_per_pixel_fmt(ctx, ctx->use_packed_pixel); + sii8620_burst_send(ctx); + sii8620_write_seq(ctx, + REG_MHL_DP_CTL0, 0xf0, + REG_MHL3_TX_ZONE_CTL, clk_spec[i].zone); + sii8620_setbits(ctx, REG_M3_P0CTRL, + BIT_M3_P0CTRL_MHL3_P0_PORT_EN + | BIT_M3_P0CTRL_MHL3_P0_UNLIMIT_EN, p0_ctrl); + sii8620_setbits(ctx, REG_M3_POSTM, MSK_M3_POSTM_RRP_DECODE, + clk_spec[i].rrp_decode); + sii8620_write_seq_static(ctx, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + | BIT_M3_CTRL_H2M_SWRST, + REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE + ); + sii8620_mt_write_stat(ctx, MHL_XDS_REG(AVLINK_MODE_CONTROL), + clk_spec[i].link_rate); + } - sii8620_write(ctx, REG_PKT_FILTER_0, 0xa1, 0x2); + sii8620_set_infoframes(ctx); } static void sii8620_start_video(struct sii8620 *ctx) { - if (ctx->mode < CM_MHL3) + if (!sii8620_is_mhl3(ctx)) sii8620_stop_video(ctx); switch (ctx->sink_type) { @@ -757,44 +1251,6 @@ static void sii8620_enable_hpd(struct sii8620 *ctx) ); } -static void sii8620_enable_gen2_write_burst(struct sii8620 *ctx) -{ - if (ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_RCV_TIMEOUT, 100, - REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN - ); - ctx->gen2_write_burst = 1; -} - -static void sii8620_disable_gen2_write_burst(struct sii8620 *ctx) -{ - if (!ctx->gen2_write_burst) - return; - - sii8620_write_seq_static(ctx, - REG_MDT_XMIT_CTRL, 0, - REG_MDT_RCV_CTRL, 0 - ); - ctx->gen2_write_burst = 0; -} - -static void sii8620_start_gen2_write_burst(struct sii8620 *ctx) -{ - sii8620_write_seq_static(ctx, - REG_MDT_INT_1_MASK, BIT_MDT_RCV_TIMEOUT - | BIT_MDT_RCV_SM_ABORT_PKT_RCVD | BIT_MDT_RCV_SM_ERROR - | BIT_MDT_XMIT_TIMEOUT | BIT_MDT_XMIT_SM_ABORT_PKT_RCVD - | BIT_MDT_XMIT_SM_ERROR, - REG_MDT_INT_0_MASK, BIT_MDT_XFIFO_EMPTY - | BIT_MDT_IDLE_AFTER_HAWB_DISABLE - | BIT_MDT_RFIFO_DATA_RDY - ); - sii8620_enable_gen2_write_burst(ctx); -} - static void sii8620_mhl_discover(struct sii8620 *ctx) { sii8620_write_seq_static(ctx, @@ -838,7 +1294,7 @@ static void sii8620_mhl_discover(struct sii8620 *ctx) static void sii8620_peer_specific_init(struct sii8620 *ctx) { - if (ctx->mode == CM_MHL3) + if (sii8620_is_mhl3(ctx)) sii8620_write_seq_static(ctx, REG_SYS_CTRL1, BIT_SYS_CTRL1_BLOCK_DDC_BY_HPD, REG_EMSCINTRMASK1, @@ -948,21 +1404,51 @@ static void sii8620_mhl_init(struct sii8620 *ctx) ); sii8620_disable_gen2_write_burst(ctx); - /* currently MHL3 is not supported, so we force version to 0 */ - sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), 0); + sii8620_mt_write_stat(ctx, MHL_DST_REG(VERSION), SII8620_MHL_VERSION); sii8620_mt_write_stat(ctx, MHL_DST_REG(CONNECTED_RDY), MHL_DST_CONN_DCAP_RDY | MHL_DST_CONN_XDEVCAPP_SUPP | MHL_DST_CONN_POW_STAT); sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), MHL_INT_RC_DCAP_CHG); } +static void sii8620_emsc_enable(struct sii8620 *ctx) +{ + u8 reg; + + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_EMSC_EN + | BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, ~0); + sii8620_setbits(ctx, REG_GENCTL, BIT_GENCTL_CLR_EMSC_RFIFO + | BIT_GENCTL_CLR_EMSC_XFIFO, 0); + sii8620_setbits(ctx, REG_COMMECNT, BIT_COMMECNT_I2C_TO_EMSC_EN, ~0); + reg = sii8620_readb(ctx, REG_EMSCINTR); + sii8620_write(ctx, REG_EMSCINTR, reg); + sii8620_write(ctx, REG_EMSCINTRMASK, BIT_EMSCINTR_SPI_DVLD); +} + +static int sii8620_wait_for_fsm_state(struct sii8620 *ctx, u8 state) +{ + int i; + + for (i = 0; i < 10; ++i) { + u8 s = sii8620_readb(ctx, REG_COC_STAT_0); + + if ((s & MSK_COC_STAT_0_FSM_STATE) == state) + return 0; + if (!(s & BIT_COC_STAT_0_PLL_LOCKED)) + return -EBUSY; + usleep_range(4000, 6000); + } + return -ETIMEDOUT; +} + static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) { + int ret; + if (ctx->mode == mode) return; - ctx->mode = mode; - switch (mode) { case CM_MHL1: sii8620_write_seq_static(ctx, @@ -972,15 +1458,46 @@ static void sii8620_set_mode(struct sii8620 *ctx, enum sii8620_mode mode) | BIT_DPD_OSC_EN, REG_COC_INTR_MASK, 0 ); + ctx->mode = mode; break; case CM_MHL3: + sii8620_write(ctx, REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE); + ctx->mode = mode; + return; + case CM_ECBUS_S: + sii8620_emsc_enable(ctx); sii8620_write_seq_static(ctx, - REG_M3_CTRL, VAL_M3_CTRL_MHL3_VALUE, - REG_COC_CTL0, 0x40, - REG_MHL_COC_CTL1, 0x07 + REG_TTXSPINUMS, 4, + REG_TRXSPINUMS, 4, + REG_TTXHSICNUMS, 0x14, + REG_TRXHSICNUMS, 0x14, + REG_TTXTOTNUMS, 0x18, + REG_TRXTOTNUMS, 0x18, + REG_PWD_SRST, BIT_PWD_SRST_COC_DOC_RST + | BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_MHL_COC_CTL1, 0xbd, + REG_PWD_SRST, BIT_PWD_SRST_CBUS_RST_SW_EN, + REG_COC_CTLB, 0x01, + REG_COC_CTL0, 0x5c, + REG_COC_CTL14, 0x03, + REG_COC_CTL15, 0x80, + REG_MHL_DP_CTL6, BIT_MHL_DP_CTL6_DP_TAP1_SGN + | BIT_MHL_DP_CTL6_DP_TAP1_EN + | BIT_MHL_DP_CTL6_DT_PREDRV_FEEDCAP_EN, + REG_MHL_DP_CTL8, 0x03 ); - break; + ret = sii8620_wait_for_fsm_state(ctx, 0x03); + sii8620_write_seq_static(ctx, + REG_COC_CTL14, 0x00, + REG_COC_CTL15, 0x80 + ); + if (!ret) + sii8620_write(ctx, REG_CBUS3_CNVT, 0x85); + else + sii8620_disconnect(ctx); + return; case CM_DISCONNECTED: + ctx->mode = mode; break; default: dev_err(ctx->dev, "%s mode %d not supported\n", __func__, mode); @@ -1007,10 +1524,12 @@ static void sii8620_disconnect(struct sii8620 *ctx) { sii8620_disable_gen2_write_burst(ctx); sii8620_stop_video(ctx); - msleep(50); + msleep(100); sii8620_cbus_reset(ctx); sii8620_set_mode(ctx, CM_DISCONNECTED); sii8620_write_seq_static(ctx, + REG_TX_ZONE_CTL1, 0, + REG_MHL_PLL_CTL0, 0x07, REG_COC_CTL0, 0x40, REG_CBUS3_CNVT, 0x84, REG_COC_CTL14, 0x00, @@ -1123,24 +1642,45 @@ static void sii8620_irq_disc(struct sii8620 *ctx) sii8620_write(ctx, REG_CBUS_DISC_INTR0, stat); } +static void sii8620_read_burst(struct sii8620 *ctx) +{ + u8 buf[17]; + + sii8620_read_buf(ctx, REG_MDT_RCV_READ_PORT, buf, ARRAY_SIZE(buf)); + sii8620_write(ctx, REG_MDT_RCV_CTRL, BIT_MDT_RCV_CTRL_MDT_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_DELAY_RCV_EN | + BIT_MDT_RCV_CTRL_MDT_RFIFO_CLR_CUR); + sii8620_readb(ctx, REG_MDT_RFIFO_STAT); +} + static void sii8620_irq_g2wb(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_MDT_INT_0); if (stat & BIT_MDT_IDLE_AFTER_HAWB_DISABLE) - dev_dbg(ctx->dev, "HAWB idle\n"); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_COMPLETE); + + if (stat & BIT_MDT_RFIFO_DATA_RDY) + sii8620_read_burst(ctx); + + if (stat & BIT_MDT_XFIFO_EMPTY) + sii8620_write(ctx, REG_MDT_XMIT_CTRL, 0); sii8620_write(ctx, REG_MDT_INT_0, stat); } -static void sii8620_status_changed_dcap(struct sii8620 *ctx) +static void sii8620_status_dcap_ready(struct sii8620 *ctx) { - if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) { - sii8620_set_mode(ctx, CM_MHL1); - sii8620_peer_specific_init(ctx); - sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE - | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); - } + enum sii8620_mode mode; + + mode = ctx->stat[MHL_DST_VERSION] >= 0x30 ? CM_MHL3 : CM_MHL1; + if (mode > ctx->mode) + sii8620_set_mode(ctx, mode); + sii8620_peer_specific_init(ctx); + sii8620_write(ctx, REG_INTR9_MASK, BIT_INTR9_DEVCAP_DONE + | BIT_INTR9_EDID_DONE | BIT_INTR9_EDID_ERROR); } static void sii8620_status_changed_path(struct sii8620 *ctx) @@ -1149,7 +1689,9 @@ static void sii8620_status_changed_path(struct sii8620 *ctx) sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL | MHL_DST_LM_PATH_ENABLED); - sii8620_mt_read_devcap(ctx, false); + if (!sii8620_is_mhl3(ctx)) + sii8620_mt_read_devcap(ctx, false); + sii8620_mt_set_cont(ctx, sii8620_sink_detected); } else { sii8620_mt_write_stat(ctx, MHL_DST_REG(LINK_MODE), MHL_DST_LM_CLK_MODE_NORMAL); @@ -1166,19 +1708,75 @@ static void sii8620_msc_mr_write_stat(struct sii8620 *ctx) sii8620_update_array(ctx->stat, st, MHL_DST_SIZE); sii8620_update_array(ctx->xstat, xst, MHL_XDS_SIZE); - if (st[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) - sii8620_status_changed_dcap(ctx); + if (ctx->stat[MHL_DST_CONNECTED_RDY] & MHL_DST_CONN_DCAP_RDY) + sii8620_status_dcap_ready(ctx); if (st[MHL_DST_LINK_MODE] & MHL_DST_LM_PATH_ENABLED) sii8620_status_changed_path(ctx); } +static void sii8620_ecbus_up(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_set_mode(ctx, CM_ECBUS_S); +} + +static void sii8620_got_ecbus_speed(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_write_stat(ctx, MHL_XDS_REG(CURR_ECBUS_MODE), + MHL_XDS_ECBUS_S | MHL_XDS_SLOT_MODE_8BIT); + sii8620_mt_rap(ctx, MHL_RAP_CBUS_MODE_UP); + sii8620_mt_set_cont(ctx, sii8620_ecbus_up); +} + +static void sii8620_mhl_burst_emsc_support_set(struct mhl_burst_emsc_support *d, + enum mhl_burst_id id) +{ + sii8620_mhl_burst_hdr_set(&d->hdr, MHL_BURST_ID_EMSC_SUPPORT); + d->num_entries = 1; + d->burst_id[0] = cpu_to_be16(id); +} + +static void sii8620_send_features(struct sii8620 *ctx) +{ + u8 buf[16]; + + sii8620_write(ctx, REG_MDT_XMIT_CTRL, BIT_MDT_XMIT_CTRL_EN + | BIT_MDT_XMIT_CTRL_FIXED_BURST_LEN); + sii8620_mhl_burst_emsc_support_set((void *)buf, + MHL_BURST_ID_HID_PAYLOAD); + sii8620_write_buf(ctx, REG_MDT_XMIT_WRITE_PORT, buf, ARRAY_SIZE(buf)); +} + static void sii8620_msc_mr_set_int(struct sii8620 *ctx) { u8 ints[MHL_INT_SIZE]; sii8620_read_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); sii8620_write_buf(ctx, REG_MHL_INT_0, ints, MHL_INT_SIZE); + + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_DCAP_CHG) { + switch (ctx->mode) { + case CM_MHL3: + sii8620_mt_read_xdevcap_reg(ctx, MHL_XDC_ECBUS_SPEEDS); + sii8620_mt_set_cont(ctx, sii8620_got_ecbus_speed); + break; + case CM_ECBUS_S: + sii8620_mt_read_devcap(ctx, true); + break; + default: + break; + } + } + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_REQ) + sii8620_send_features(ctx); + if (ints[MHL_INT_RCHANGE] & MHL_INT_RC_FEAT_COMPLETE) + sii8620_edid_read(ctx, 0); } static struct sii8620_mt_msg *sii8620_msc_msg_first(struct sii8620 *ctx) @@ -1261,6 +1859,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); } @@ -1289,17 +1900,6 @@ static void sii8620_scdt_high(struct sii8620 *ctx) ); } -static void sii8620_scdt_low(struct sii8620 *ctx) -{ - sii8620_write(ctx, REG_TMDS_CSTAT_P3, - BIT_TMDS_CSTAT_P3_SCDT_CLR_AVI_DIS | - BIT_TMDS_CSTAT_P3_CLR_AVI); - - sii8620_stop_video(ctx); - - sii8620_write(ctx, REG_INTR8_MASK, 0); -} - static void sii8620_irq_scdt(struct sii8620 *ctx) { u8 stat = sii8620_readb(ctx, REG_INTR5); @@ -1309,8 +1909,6 @@ static void sii8620_irq_scdt(struct sii8620 *ctx) if (cstat & BIT_TMDS_CSTAT_P3_SCDT) sii8620_scdt_high(ctx); - else - sii8620_scdt_low(ctx); } sii8620_write(ctx, REG_INTR5, stat); @@ -1351,6 +1949,65 @@ static void sii8620_irq_infr(struct sii8620 *ctx) sii8620_start_video(ctx); } +static void sii8620_got_xdevcap(struct sii8620 *ctx, int ret) +{ + if (ret < 0) + return; + + sii8620_mt_read_devcap(ctx, false); +} + +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); + sii8620_mt_set_cont(ctx, sii8620_got_xdevcap); + } 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); +} + +static void sii8620_irq_ddc(struct sii8620 *ctx) +{ + u8 stat = sii8620_readb(ctx, REG_INTR3); + + if (stat & BIT_DDC_CMD_DONE) { + sii8620_write(ctx, REG_INTR3_MASK, 0); + if (sii8620_is_mhl3(ctx)) + sii8620_mt_set_int(ctx, MHL_INT_REG(RCHANGE), + MHL_INT_RC_FEAT_REQ); + else + sii8620_edid_read(ctx, 0); + } + sii8620_write(ctx, REG_INTR3, stat); +} + /* endian agnostic, non-volatile version of test_bit */ static bool sii8620_test_bit(unsigned int nr, const u8 *addr) { @@ -1366,9 +2023,12 @@ 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_DDC, sii8620_irq_ddc }, { BIT_FAST_INTR_STAT_SCDT, sii8620_irq_scdt }, { BIT_FAST_INTR_STAT_INFR, sii8620_irq_infr }, }; @@ -1383,7 +2043,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) { @@ -1450,22 +2112,41 @@ static bool sii8620_mode_fixup(struct drm_bridge *bridge, struct drm_display_mode *adjusted_mode) { struct sii8620 *ctx = bridge_to_sii8620(bridge); - bool ret = false; - int max_clock = 74250; + int max_lclk; + bool ret = true; mutex_lock(&ctx->lock); - if (mode->flags & DRM_MODE_FLAG_INTERLACE) - goto out; - - if (ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) - max_clock = 300000; - - ret = mode->clock <= max_clock; - -out: + max_lclk = sii8620_is_mhl3(ctx) ? MHL3_MAX_LCLK : MHL1_MAX_LCLK; + if (max_lclk > 3 * adjusted_mode->clock) { + ctx->use_packed_pixel = 0; + goto end; + } + if ((ctx->devcap[MHL_DCAP_VID_LINK_MODE] & MHL_DCAP_VID_LINK_PPIXEL) && + max_lclk > 2 * adjusted_mode->clock) { + ctx->use_packed_pixel = 1; + goto end; + } + ret = false; +end: + if (ret) { + u8 vic = drm_match_cea_mode(adjusted_mode); + + if (!vic) { + union hdmi_infoframe frm; + u8 mhl_vic[] = { 0, 95, 94, 93, 98 }; + + drm_hdmi_vendor_infoframe_from_display_mode( + &frm.vendor.hdmi, adjusted_mode); + vic = frm.vendor.hdmi.vic; + if (vic >= ARRAY_SIZE(mhl_vic)) + vic = 0; + vic = mhl_vic[vic]; + } + ctx->video_code = vic; + ctx->pixel_clock = adjusted_mode->clock; + } mutex_unlock(&ctx->lock); - return ret; } |