Files
android_kernel_samsung_sm86…/msm/dsi/dsi_ctrl_hw_2_2.c
Satya Rama Aditya Pinapala ae5b602b4f disp: msm: dsi: trigger broadcast commands using DMA start window
As per the HW requirements it is highly recommended to use DMA start
window to trigger  broadcast commands. If not used then it can
result in a hardware hang with the DSI controllers going out
of sync. This behavior is even more prominent in cases of higher
refresh rates. As part of the change we change the default DMA
scheduling behavior to default to maximum possible DMA window
in case it is not specified in the panel device tree.

Change-Id: Ied4df9063664cedbc18ce009054d4e5ecae30ab2
Signed-off-by: Satya Rama Aditya Pinapala <psraditya30@codeaurora.org>
2020-12-04 20:01:55 -08:00

283 lines
7.4 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
#include "dsi_catalog.h"
#define DISP_CC_MISC_CMD_REG_OFF 0x00
/* register to configure DMA scheduling */
#define DSI_DMA_SCHEDULE_CTRL 0x100
#define DSI_DMA_SCHEDULE_CTRL2 0x0104
/* offset addresses of MDP INTF base register, to be mapped for debug feature */
#define MDP_INTF_TEAR_OFFSET 0x280
#define MDP_INTF_TEAR_LINE_COUNT_OFFSET 0x30
#define MDP_INTF_LINE_COUNT_OFFSET 0xB0
void dsi_ctrl_hw_22_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map)
{
u32 reg_value = lane_map->lane_map_v2[DSI_LOGICAL_LANE_0] |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_1] << 4) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_2] << 8) |
(lane_map->lane_map_v2[DSI_LOGICAL_LANE_3] << 12);
DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, reg_value);
DSI_CTRL_HW_DBG(ctrl, "[DSI_%d] Lane swap setup complete\n",
ctrl->index);
}
int dsi_ctrl_hw_22_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl,
u32 lanes)
{
int rc = 0, val = 0;
u32 fifo_empty_mask = 0;
u32 const sleep_us = 10;
u32 const timeout_us = 100;
if (lanes & DSI_DATA_LANE_0)
fifo_empty_mask |= (BIT(12) | BIT(16));
if (lanes & DSI_DATA_LANE_1)
fifo_empty_mask |= BIT(20);
if (lanes & DSI_DATA_LANE_2)
fifo_empty_mask |= BIT(24);
if (lanes & DSI_DATA_LANE_3)
fifo_empty_mask |= BIT(28);
DSI_CTRL_HW_DBG(ctrl, "%s: polling for fifo empty, mask=0x%08x\n",
__func__, fifo_empty_mask);
rc = readl_poll_timeout(ctrl->base + DSI_FIFO_STATUS, val,
(val & fifo_empty_mask), sleep_us, timeout_us);
if (rc) {
DSI_CTRL_HW_ERR(ctrl,
"%s: fifo not empty, FIFO_STATUS=0x%08x\n",
__func__, val);
goto error;
}
error:
return rc;
}
ssize_t dsi_ctrl_hw_22_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size)
{
return size;
}
/**
* dsi_ctrl_hw_22_phy_reset_config() - to configure clamp control during ulps
* @ctrl: Pointer to the controller host hardware.
* @enable: boolean to specify enable/disable.
*/
void dsi_ctrl_hw_22_phy_reset_config(struct dsi_ctrl_hw *ctrl,
bool enable)
{
u32 reg = 0;
reg = DSI_DISP_CC_R32(ctrl, DISP_CC_MISC_CMD_REG_OFF);
/* Mask/unmask disable PHY reset bit */
if (enable)
reg &= ~BIT(ctrl->index);
else
reg |= BIT(ctrl->index);
DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg);
}
/**
* dsi_ctrl_hw_22_schedule_dma_cmd() - to schedule DMA command transfer
* @ctrl: Pointer to the controller host hardware.
* @line_no: Line number at which command needs to be sent.
*/
void dsi_ctrl_hw_22_schedule_dma_cmd(struct dsi_ctrl_hw *ctrl, int line_no)
{
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_DMA_SCHEDULE_CTRL);
reg |= BIT(28);
reg |= (line_no & 0xffff);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL, reg);
ctrl->reset_trig_ctrl = true;
}
/*
* dsi_ctrl_hw_kickoff_non_embedded_mode()-Kickoff cmd in non-embedded mode
* @ctrl: - Pointer to the controller host hardware.
* @dsi_ctrl_cmd_dma_info: - command buffer information.
* @flags: - DSI CTRL Flags.
*/
void dsi_ctrl_hw_kickoff_non_embedded_mode(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 flags)
{
u32 reg = 0;
reg = DSI_R32(ctrl, DSI_COMMAND_MODE_DMA_CTRL);
reg &= ~BIT(31);/* disable broadcast */
reg &= ~BIT(30);
if (cmd->use_lpm)
reg |= BIT(26);
else
reg &= ~BIT(26);
/* Select non EMBEDDED_MODE, pick the packet header from register */
reg &= ~BIT(28);
reg |= BIT(24);/* long packet */
reg |= BIT(29);/* wc_sel = 1 */
reg |= (((cmd->datatype) & 0x03f) << 16);/* data type */
DSI_W32(ctrl, DSI_COMMAND_MODE_DMA_CTRL, reg);
/* Enable WRITE_WATERMARK_DISABLE and READ_WATERMARK_DISABLE bits */
reg = DSI_R32(ctrl, DSI_DMA_FIFO_CTRL);
reg |= BIT(20);
reg |= BIT(16);
reg |= 0x33;/* Set READ and WRITE watermark levels to maximum */
DSI_W32(ctrl, DSI_DMA_FIFO_CTRL, reg);
DSI_W32(ctrl, DSI_DMA_CMD_OFFSET, cmd->offset);
DSI_W32(ctrl, DSI_DMA_CMD_LENGTH, ((cmd->length) & 0xFFFFFF));
/* wait for writes to complete before kick off */
wmb();
if (!(flags & DSI_CTRL_HW_CMD_WAIT_FOR_TRIGGER))
DSI_W32(ctrl, DSI_CMD_MODE_DMA_SW_TRIGGER, 0x1);
}
/*
* dsi_ctrl_hw_22_config_clk_gating() - enable/disable clk gating on DSI PHY
* @ctrl: Pointer to the controller host hardware.
* @enable: bool to notify enable/disable.
* @clk_selection: clock to enable/disable clock gating.
*
*/
void dsi_ctrl_hw_22_config_clk_gating(struct dsi_ctrl_hw *ctrl, bool enable,
enum dsi_clk_gate_type clk_selection)
{
u32 reg = 0;
u32 enable_select = 0;
reg = DSI_DISP_CC_R32(ctrl, DISP_CC_MISC_CMD_REG_OFF);
if (clk_selection & PIXEL_CLK)
enable_select |= ctrl->index ? BIT(6) : BIT(5);
if (clk_selection & BYTE_CLK)
enable_select |= ctrl->index ? BIT(8) : BIT(7);
if (clk_selection & DSI_PHY)
enable_select |= ctrl->index ? BIT(10) : BIT(9);
if (enable)
reg |= enable_select;
else
reg &= ~enable_select;
DSI_DISP_CC_W32(ctrl, DISP_CC_MISC_CMD_REG_OFF, reg);
}
/**
* dsi_ctrl_hw_22_configure_cmddma_window() - configure DMA window for CMD TX
* @ctrl: Pointer to the controller host hardware.
* @cmd: Pointer to the DSI DMA command info.
* @line_no: Line number at which the CMD needs to be triggered.
* @window: Width of the DMA CMD window.
*/
void dsi_ctrl_hw_22_configure_cmddma_window(struct dsi_ctrl_hw *ctrl,
struct dsi_ctrl_cmd_dma_info *cmd,
u32 line_no, u32 window)
{
u32 reg = 0;
if (cmd->en_broadcast) {
reg = DSI_R32(ctrl, DSI_TRIG_CTRL);
if (cmd->is_master) {
reg &= ~0xF;
reg |= 0xc;
} else {
reg &= ~0xF;
reg |= BIT(16);
}
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
if (cmd->is_master) {
reg = 0;
reg |= line_no;
reg |= window << 16;
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, reg);
}
} else {
reg = DSI_R32(ctrl, DSI_TRIG_CTRL);
reg &= ~0xF;
reg |= 0xc;
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
reg = 0;
reg |= line_no;
reg |= window << 16;
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, reg);
}
ctrl->reset_trig_ctrl = true;
}
/**
* dsi_ctrl_hw_22_reset_trigger_controls() - reset dsi trigger configurations
* @ctrl: Pointer to the controller host hardware.
* @cfg: DSI host configuration that is common to both video and
* command modes.
*/
void dsi_ctrl_hw_22_reset_trigger_controls(struct dsi_ctrl_hw *ctrl,
struct dsi_host_common_cfg *cfg)
{
u32 reg = 0;
const u8 trigger_map[DSI_TRIGGER_MAX] = {
0x0, 0x2, 0x1, 0x4, 0x5, 0x6 };
reg |= (cfg->te_mode == DSI_TE_ON_EXT_PIN) ? BIT(31) : 0;
reg |= (trigger_map[cfg->dma_cmd_trigger] & 0x7);
reg |= (trigger_map[cfg->mdp_cmd_trigger] & 0x7) << 4;
DSI_W32(ctrl, DSI_TRIG_CTRL, reg);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL2, 0x0);
DSI_W32(ctrl, DSI_DMA_SCHEDULE_CTRL, 0x0);
ctrl->reset_trig_ctrl = false;
}
/**
* 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 (IS_ERR_OR_NULL(ctrl->mdp_intf_base))
return reg;
if (cmd_mode)
reg = readl_relaxed(ctrl->mdp_intf_base + MDP_INTF_TEAR_OFFSET
+ MDP_INTF_TEAR_LINE_COUNT_OFFSET);
else
reg = readl_relaxed(ctrl->mdp_intf_base
+ MDP_INTF_LINE_COUNT_OFFSET);
return reg;
}