diff --git a/msm/dsi/dsi_catalog.c b/msm/dsi/dsi_catalog.c index f26c72964e5a..19fb900c1475 100644 --- a/msm/dsi/dsi_catalog.c +++ b/msm/dsi/dsi_catalog.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #include @@ -81,6 +81,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.schedule_dma_cmd = NULL; ctrl->ops.kickoff_command_non_embedded_mode = NULL; ctrl->ops.config_clk_gating = NULL; + ctrl->ops.map_mdp_regs = NULL; + ctrl->ops.log_line_count = NULL; break; case DSI_CTRL_VERSION_2_0: ctrl->ops.setup_lane_map = dsi_ctrl_hw_20_setup_lane_map; @@ -96,6 +98,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.schedule_dma_cmd = NULL; ctrl->ops.kickoff_command_non_embedded_mode = NULL; ctrl->ops.config_clk_gating = NULL; + ctrl->ops.map_mdp_regs = NULL; + ctrl->ops.log_line_count = NULL; break; case DSI_CTRL_VERSION_2_2: case DSI_CTRL_VERSION_2_3: @@ -116,6 +120,8 @@ static void dsi_catalog_cmn_init(struct dsi_ctrl_hw *ctrl, ctrl->ops.schedule_dma_cmd = dsi_ctrl_hw_22_schedule_dma_cmd; ctrl->ops.kickoff_command_non_embedded_mode = dsi_ctrl_hw_kickoff_non_embedded_mode; + ctrl->ops.map_mdp_regs = dsi_ctrl_hw_22_map_mdp_regs; + ctrl->ops.log_line_count = dsi_ctrl_hw_22_log_line_count; break; default: break; diff --git a/msm/dsi/dsi_catalog.h b/msm/dsi/dsi_catalog.h index ed047e9ef86d..3118cb573301 100644 --- a/msm/dsi/dsi_catalog.h +++ b/msm/dsi/dsi_catalog.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2015-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2020, The Linux Foundation. All rights reserved. */ #ifndef _DSI_CATALOG_H_ @@ -270,4 +270,9 @@ void dsi_phy_hw_v4_0_dyn_refresh_pipe_delay(struct dsi_phy_hw *phy, int dsi_phy_hw_v4_0_cache_phy_timings(struct dsi_phy_per_lane_cfgs *timings, u32 *dst, u32 size); + +int dsi_ctrl_hw_22_map_mdp_regs(struct platform_device *pdev, + struct dsi_ctrl_hw *ctrl); + +u32 dsi_ctrl_hw_22_log_line_count(struct dsi_ctrl_hw *ctrl, bool cmd_mode); #endif /* _DSI_CATALOG_H_ */ diff --git a/msm/dsi/dsi_ctrl.c b/msm/dsi/dsi_ctrl.c index b085fce52f22..bf316d5dfbe5 100644 --- a/msm/dsi/dsi_ctrl.c +++ b/msm/dsi/dsi_ctrl.c @@ -1219,6 +1219,24 @@ int dsi_message_validate_tx_mode(struct dsi_ctrl *dsi_ctrl, return rc; } +static u32 calculate_schedule_line(struct dsi_ctrl *dsi_ctrl, u32 flags) +{ + u32 line_no = 0x1; + struct dsi_mode_info *timing; + + /* check if custom dma scheduling line needed */ + if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) && + (flags & DSI_CTRL_CMD_CUSTOM_DMA_SCHED)) + line_no = dsi_ctrl->host_config.u.video_engine.dma_sched_line; + + timing = &(dsi_ctrl->host_config.video_timing); + + if (timing) + line_no += timing->v_back_porch + timing->v_sync_width + + timing->v_active; + + return line_no; +} static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, const struct mipi_dsi_msg *msg, @@ -1228,20 +1246,13 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, { u32 hw_flags = 0; u32 line_no = 0x1; - struct dsi_mode_info *timing; struct dsi_ctrl_hw_ops dsi_hw_ops = dsi_ctrl->hw.ops; SDE_EVT32(dsi_ctrl->cell_index, SDE_EVTLOG_FUNC_ENTRY, flags, msg->flags); - /* check if custom dma scheduling line needed */ - if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) && - (flags & DSI_CTRL_CMD_CUSTOM_DMA_SCHED)) - line_no = dsi_ctrl->host_config.u.video_engine.dma_sched_line; - timing = &(dsi_ctrl->host_config.video_timing); - if (timing) - line_no += timing->v_back_porch + timing->v_sync_width + - timing->v_active; + line_no = calculate_schedule_line(dsi_ctrl, flags); + if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) && dsi_hw_ops.schedule_dma_cmd && (dsi_ctrl->current_state.vid_engine_state == @@ -1249,6 +1260,8 @@ static void dsi_kickoff_msg_tx(struct dsi_ctrl *dsi_ctrl, dsi_hw_ops.schedule_dma_cmd(&dsi_ctrl->hw, line_no); + dsi_ctrl->cmd_mode = (dsi_ctrl->host_config.panel_mode == + DSI_OP_CMD_MODE); hw_flags |= (flags & DSI_CTRL_CMD_DEFER_TRIGGER) ? DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER : 0; @@ -1967,6 +1980,9 @@ static int dsi_ctrl_dev_probe(struct platform_device *pdev) DSI_CTRL_DEBUG(dsi_ctrl, "failed to init axi bus client, rc = %d\n", rc); + if (dsi_ctrl->hw.ops.map_mdp_regs) + dsi_ctrl->hw.ops.map_mdp_regs(pdev, &dsi_ctrl->hw); + item->ctrl = dsi_ctrl; mutex_lock(&dsi_ctrl_list_lock); @@ -3298,6 +3314,10 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) { int rc = 0; struct dsi_ctrl_hw_ops dsi_hw_ops; + u32 v_total = 0, fps = 0, cur_line = 0, mem_latency_us = 100; + u32 line_time = 0, schedule_line = 0x1, latency_by_line = 0; + struct dsi_mode_info *timing; + unsigned long flag; if (!dsi_ctrl) { DSI_CTRL_ERR(dsi_ctrl, "Invalid params\n"); @@ -3313,6 +3333,18 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) mutex_lock(&dsi_ctrl->ctrl_lock); + timing = &(dsi_ctrl->host_config.video_timing); + + if (timing && + (dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE)) { + v_total = timing->v_sync_width + timing->v_back_porch + + timing->v_front_porch + timing->v_active; + fps = timing->refresh_rate; + schedule_line = calculate_schedule_line(dsi_ctrl, flags); + line_time = (1000000 / fps) / v_total; + latency_by_line = CEIL(mem_latency_us, line_time); + } + if (!(flags & DSI_CTRL_CMD_BROADCAST_MASTER)) dsi_hw_ops.trigger_command_dma(&dsi_ctrl->hw); @@ -3325,7 +3357,36 @@ int dsi_ctrl_cmd_tx_trigger(struct dsi_ctrl *dsi_ctrl, u32 flags) reinit_completion(&dsi_ctrl->irq_info.cmd_dma_done); /* trigger command */ - dsi_hw_ops.trigger_command_dma(&dsi_ctrl->hw); + if ((dsi_ctrl->host_config.panel_mode == DSI_OP_VIDEO_MODE) && + dsi_hw_ops.schedule_dma_cmd && + (dsi_ctrl->current_state.vid_engine_state == + DSI_CTRL_ENGINE_ON)) { + /* + * This change reads the video line count from + * MDP_INTF_LINE_COUNT register and checks whether + * DMA trigger happens close to the schedule line. + * If it is not close to the schedule line, then DMA + * command transfer is triggered. + */ + while (1) { + local_irq_save(flag); + cur_line = + dsi_hw_ops.log_line_count(&dsi_ctrl->hw, + dsi_ctrl->cmd_mode); + if (cur_line < + (schedule_line - latency_by_line) || + cur_line > (schedule_line + 1)) { + dsi_hw_ops.trigger_command_dma( + &dsi_ctrl->hw); + local_irq_restore(flag); + break; + } + local_irq_restore(flag); + udelay(1000); + } + } else + dsi_hw_ops.trigger_command_dma(&dsi_ctrl->hw); + if (flags & DSI_CTRL_CMD_ASYNC_WAIT) { dsi_ctrl->dma_wait_queued = true; queue_work(dsi_ctrl->dma_cmd_workq, diff --git a/msm/dsi/dsi_ctrl.h b/msm/dsi/dsi_ctrl.h index c0348092e9eb..3f73ae114cd4 100644 --- a/msm/dsi/dsi_ctrl.h +++ b/msm/dsi/dsi_ctrl.h @@ -239,6 +239,8 @@ struct dsi_ctrl_interrupts { * insert null packet. * @modeupdated: Boolean to send new roi if mode is updated. * @split_link_supported: Boolean to check if hw supports split link. + * @cmd_mode: Boolean to indicate if panel is running in + command mode. */ struct dsi_ctrl { struct platform_device *pdev; @@ -299,6 +301,7 @@ struct dsi_ctrl { bool null_insertion_enabled; bool modeupdated; bool split_link_supported; + bool cmd_mode; }; /** diff --git a/msm/dsi/dsi_ctrl_hw.h b/msm/dsi/dsi_ctrl_hw.h index 9411e98c0fb6..a6c7f2fdfef3 100644 --- a/msm/dsi/dsi_ctrl_hw.h +++ b/msm/dsi/dsi_ctrl_hw.h @@ -838,6 +838,22 @@ struct dsi_ctrl_hw_ops { * @sel_phy: Bool to control whether to select phy or controller */ void (*hs_req_sel)(struct dsi_ctrl_hw *ctrl, bool sel_phy); + + /** + * hw.ops.map_mdp_regs() - maps MDP interface line count registers. + * @pdev:» Pointer to platform device. + * @ctrl:» Pointer to the controller host hardware. + */ + int (*map_mdp_regs)(struct platform_device *pdev, + struct dsi_ctrl_hw *ctrl); + + /** + * hw.ops.log_line_count() - reads the MDP interface line count + * registers. + * @ctrl:» Pointer to the controller host hardware. + * @cmd_mode:» Boolean to indicate command mode operation. + */ + u32 (*log_line_count)(struct dsi_ctrl_hw *ctrl, bool cmd_mode); }; /* @@ -848,6 +864,13 @@ struct dsi_ctrl_hw_ops { * @mmss_misc_length: Length of mmss_misc register map. * @disp_cc_base: Base address of disp_cc register map. * @disp_cc_length: Length of disp_cc register map. + * @te_rd_ptr_reg: Address of MDP_TEAR_INTF_TEAR_LINE_COUNT. This + * register is used for testing and validating the RD + * ptr value when a CMD is triggered and it succeeds. + * @line_count_reg: Address of MDP_TEAR_INTF_LINE_COUNT. This + * register is used for testing and validating the + * line count value when a CMD is triggered and it + * succeeds. * @index: Instance ID of the controller. * @feature_map: Features supported by the DSI controller. * @ops: Function pointers to the operations supported by the @@ -865,6 +888,8 @@ struct dsi_ctrl_hw { void __iomem *mmss_misc_base; u32 mmss_misc_length; void __iomem *disp_cc_base; + void __iomem *te_rd_ptr_reg; + void __iomem *line_count_reg; u32 disp_cc_length; u32 index; diff --git a/msm/dsi/dsi_ctrl_hw_2_2.c b/msm/dsi/dsi_ctrl_hw_2_2.c index 0c6d3404a268..56821e857a91 100644 --- a/msm/dsi/dsi_ctrl_hw_2_2.c +++ b/msm/dsi/dsi_ctrl_hw_2_2.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved. + * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. */ #include "dsi_ctrl_hw.h" @@ -13,6 +13,10 @@ /* register to configure DMA scheduling */ #define DSI_DMA_SCHEDULE_CTRL 0x100 +/* MDP INTF registers to be mapped*/ +#define MDP_INTF1_TEAR_LINE_COUNT 0xAE6BAB0 +#define MDP_INTF1_LINE_COUNT 0xAE6B8B0 + /** * dsi_ctrl_hw_22_phy_reset_config() - to configure clamp control during ulps * @ctrl: Pointer to the controller host hardware. @@ -126,3 +130,61 @@ void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable, DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg); } + +/** + * dsi_ctrl_hw_22_map_mdp_regs() - maps MDP interface line count registers. + * @pdev: Pointer to platform device. + * @ctrl: Pointer to the controller host hardware. + * + * Return: 0 on success and error on failure. + */ +int dsi_ctrl_hw_22_map_mdp_regs(struct platform_device *pdev, + struct dsi_ctrl_hw *ctrl) +{ + int rc = 0; + void __iomem *ptr = NULL, *ptr1 = NULL; + + if (ctrl->index == 0) { + ptr = devm_ioremap(&pdev->dev, MDP_INTF1_TEAR_LINE_COUNT, 1); + if (IS_ERR_OR_NULL(ptr)) { + DSI_CTRL_HW_ERR(ctrl, + "MDP TE LINE COUNT address not found\n"); + rc = PTR_ERR(ptr); + return rc; + } + + ptr1 = devm_ioremap(&pdev->dev, MDP_INTF1_LINE_COUNT, 1); + if (IS_ERR_OR_NULL(ptr1)) { + DSI_CTRL_HW_ERR(ctrl, + "MDP TE LINE COUNT address not found\n"); + rc = PTR_ERR(ptr1); + return rc; + } + } + + ctrl->te_rd_ptr_reg = ptr; + ctrl->line_count_reg = ptr1; + + return rc; +} + +/** + * dsi_ctrl_hw_22_log_line_count() - reads the MDP interface line count + * registers. + * @ctrl: Pointer to the controller host hardware. + * @cmd_mode: Boolean to indicate command mode operation. + * + * Return: INTF register value. + */ +u32 dsi_ctrl_hw_22_log_line_count(struct dsi_ctrl_hw *ctrl, bool cmd_mode) +{ + + u32 reg = 0; + + if (cmd_mode && ctrl->te_rd_ptr_reg) + reg = readl_relaxed(ctrl->te_rd_ptr_reg); + else if (ctrl->line_count_reg) + reg = readl_relaxed(ctrl->line_count_reg); + + return reg; +} diff --git a/msm/dsi/dsi_ctrl_hw_cmn.c b/msm/dsi/dsi_ctrl_hw_cmn.c index caa1f86591c5..e3e715c2f41c 100644 --- a/msm/dsi/dsi_ctrl_hw_cmn.c +++ b/msm/dsi/dsi_ctrl_hw_cmn.c @@ -778,7 +778,6 @@ void dsi_ctrl_hw_cmn_reset_cmd_fifo(struct dsi_ctrl_hw *ctrl) void dsi_ctrl_hw_cmn_trigger_command_dma(struct dsi_ctrl_hw *ctrl) { DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1); - DSI_CTRL_HW_DBG(ctrl, "CMD DMA triggered\n"); } /**