diff options
author | Jiun Yu <jiun.yu@samsung.com> | 2021-02-03 18:19:46 +0900 |
---|---|---|
committer | TreeHugger Robot <treehugger-gerrit@google.com> | 2021-02-10 01:02:12 +0000 |
commit | 31ae2e261475c23a37734a00f77c3a791ece6a8e (patch) | |
tree | 2f863f39196eee8f143ec55a12988f68362766be /samsung/exynos_drm_dsim.c | |
parent | 805788840f359a9752ea01d96ce9cd5b852c626d (diff) | |
download | display-31ae2e261475c23a37734a00f77c3a791ece6a8e.tar.gz |
drm: samsung: send multi dsi command at once
When sending a command, if LASTCOMMAND is not set in flags,
the command must be stacked and sent at once. At this time,
the command(s) should be sent all in one vblank period.
Bug: 177100504
Signed-off-by: Jiun Yu <jiun.yu@samsung.com>
Change-Id: I5b26ff42ab356402a14a57b878f0ea4d0e37c523
Diffstat (limited to 'samsung/exynos_drm_dsim.c')
-rw-r--r-- | samsung/exynos_drm_dsim.c | 142 |
1 files changed, 139 insertions, 3 deletions
diff --git a/samsung/exynos_drm_dsim.c b/samsung/exynos_drm_dsim.c index 90a5dda..63a7cbd 100644 --- a/samsung/exynos_drm_dsim.c +++ b/samsung/exynos_drm_dsim.c @@ -252,6 +252,8 @@ void dsim_enter_ulps(struct dsim_device *dsim) /* Wait for current read & write CMDs. */ mutex_lock(&dsim->cmd_lock); + if (WARN_ON(dsim_reg_has_pend_cmd(dsim->id))) + dsim_dump(dsim); dsim->state = DSIM_STATE_ULPS; mutex_unlock(&dsim->cmd_lock); @@ -289,6 +291,9 @@ static void dsim_disable(struct drm_encoder *encoder) mutex_lock(&dsim->cmd_lock); dsim->state = DSIM_STATE_SUSPEND; mutex_unlock(&dsim->cmd_lock); + + WARN_ON(dsim_reg_has_pend_cmd(dsim->id)); + mutex_unlock(&dsim->state_lock); dsim_set_te_pinctrl(dsim, 0); @@ -1475,12 +1480,15 @@ static void __dsim_write_data(struct dsim_device *dsim, dsim_write_payload(dsim, packet.payload, packet.payload_length); dsim_reg_wr_tx_header(dsim->id, packet.header[0], packet.header[1], packet.header[2], false); + + dsim_debug(dsim, "header(0x%x 0x%x 0x%x) size(%lu) ph fifo(%d)\n", + packet.header[0], packet.header[1], packet.header[2], + packet.size, dsim_reg_get_ph_cnt(dsim->id)); } -static int -dsim_write_data(struct dsim_device *dsim, const struct mipi_dsi_msg *msg) +static int dsim_write_single_cmd_unlocked(struct dsim_device *dsim, + const struct mipi_dsi_msg *msg, bool is_long) { - bool is_long = mipi_dsi_packet_format_is_long(msg->type); const u8 *tx_buf = msg->tx_buf; if (dsim->state != DSIM_STATE_HSCLKEN) { @@ -1499,6 +1507,134 @@ dsim_write_data(struct dsim_device *dsim, const struct mipi_dsi_msg *msg) return dsim_wait_for_cmd_fifo_empty(dsim, is_long); } +/* + * <-- ACTIVE --> + * data transfer ---|-**************------------------|-- + * <-CMD ALLOW-> + * CMD LOCK __|-----------------|_____________|--- + * + * TE PROTECT <------- TE PROTECT ON --------> + * + * ready allow <---- ready allow ---->|<-1ms->| + * + * It is important to set packet-go ready to high to send multiple commnads + * within one vblank interval at the same time. Because as soon as it becomes + * high, the piled commands will start transmission. The ready_allow_period is + * defined so that the stacked commands would not be sent in two vblank + * intervals. The ready_allow_period is set from start of vblank to end of + * CMD ALLOW period - 1ms. The 1ms is enough time to send the stacked commands + * at once. + */ +#define PKTGO_READY_MARGIN_NS 1000000 +static void need_wait_vblank(struct dsim_device *dsim) +{ + const struct decon_device *decon = dsim_get_decon(dsim); + struct drm_vblank_crtc *vblank; + struct drm_crtc *crtc; + ktime_t last_vblanktime, diff, cur_time; + int ready_allow_period; + + if (!decon) + return; + + crtc = &decon->crtc->base; + if (!crtc) + return; + + vblank = &crtc->dev->vblank[crtc->index]; + ready_allow_period = + mult_frac(vblank->framedur_ns, 95, 100) - PKTGO_READY_MARGIN_NS; + + drm_crtc_vblank_count_and_time(crtc, &last_vblanktime); + cur_time = ktime_get(); + diff = ktime_sub_ns(cur_time, last_vblanktime); + + dsim_debug(dsim, "last(%lld) cur(%lld) diff(%lld) ready allow period(%d)\n", + last_vblanktime, cur_time, diff, ready_allow_period); + + if (diff > ready_allow_period) + drm_crtc_wait_one_vblank(crtc); +} + +#define PL_FIFO_THRESHOLD mult_frac(MAX_PL_FIFO, 75, 100) /* 75% */ +#define IS_LAST(flags) (((flags) & MIPI_DSI_MSG_LASTCOMMAND) != 0) +static int +dsim_write_data(struct dsim_device *dsim, const struct mipi_dsi_msg *msg) +{ + int ret = 0; + const struct decon_device *decon = dsim_get_decon(dsim); + u16 flags = msg->flags; + bool is_long = mipi_dsi_packet_format_is_long(msg->type); + + if (dsim->config.mode == DSIM_VIDEO_MODE) { + ret = dsim_write_single_cmd_unlocked(dsim, msg, is_long); + goto err; + } + + if (((dsim->total_pend_pl + msg->tx_len) > MAX_PL_FIFO) || + (dsim->total_pend_ph == MAX_PH_FIFO)) { + dsim_err(dsim, "fifo would be full. ph(%u) pl(%lu) max(%d/%d)\n", + dsim->total_pend_ph, + dsim->total_pend_pl + msg->tx_len, + MAX_PH_FIFO, MAX_PL_FIFO); + ret = -EINVAL; + goto err; + } + + if (!IS_LAST(msg->flags) && + (((dsim->total_pend_ph + 1) == MAX_PH_FIFO) || + ((dsim->total_pend_pl + msg->tx_len) > PL_FIFO_THRESHOLD))) { + dsim_warn(dsim, "warning. changed last command. pend pl/pl(%u,%u)\n", + dsim->total_pend_ph, dsim->total_pend_pl); + flags |= MIPI_DSI_MSG_LASTCOMMAND; + } + + dsim_debug(dsim, "%s last command\n", IS_LAST(flags) ? "" : "Not"); + + if (IS_LAST(flags)) { + if (dsim->total_pend_ph) { + reinit_completion(is_long ? + &dsim->pl_wr_comp : &dsim->ph_wr_comp); + + __dsim_write_data(dsim, msg, is_long); + + need_wait_vblank(dsim); + + dsim_reg_ready_packetgo(dsim->id, true); + dsim_debug(dsim, "packet go ready\n"); + + ret = dsim_wait_for_cmd_fifo_empty(dsim, is_long); + if (ret) { + if (decon) + hibernation_unblock_enter(decon->hibernation); + return ret; + } + + dsim_reg_enable_packetgo(dsim->id, false); + dsim->total_pend_ph = 0; + dsim->total_pend_pl = 0; + if (decon) + hibernation_unblock_enter(decon->hibernation); + } else { + ret = dsim_write_single_cmd_unlocked(dsim, msg, is_long); + } + } else { + if (!dsim->total_pend_ph) { + if (decon) + hibernation_block_exit(decon->hibernation); + dsim_reg_enable_packetgo(dsim->id, true); + } + dsim->total_pend_ph++; + dsim->total_pend_pl += ALIGN(msg->tx_len, 4); + __dsim_write_data(dsim, msg, is_long); + dsim_debug(dsim, "total pending packet header(%u) payload(%u)\n", + dsim->total_pend_ph, dsim->total_pend_pl); + } + +err: + return ret; +} + static int dsim_req_read_command(struct dsim_device *dsim, const struct mipi_dsi_msg *msg) { |