blob: 37473b82cdeaf87a099eaad309fea56c7a7500eb [file] [log] [blame]
/*
* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 and
* only version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#define pr_fmt(fmt) "dsi-hw:" fmt
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "dsi_ctrl_hw.h"
#include "dsi_ctrl_reg.h"
#include "dsi_hw.h"
#define MMSS_MISC_CLAMP_REG_OFF 0x0014
/**
* dsi_ctrl_hw_14_setup_lane_map() - setup mapping between
* logical and physical lanes
* @ctrl: Pointer to the controller host hardware.
* @lane_map: Structure defining the mapping between DSI logical
* lanes and physical lanes.
*/
void dsi_ctrl_hw_14_setup_lane_map(struct dsi_ctrl_hw *ctrl,
struct dsi_lane_map *lane_map)
{
DSI_W32(ctrl, DSI_LANE_SWAP_CTRL, lane_map->lane_map_v1);
pr_debug("[DSI_%d] Lane swap setup complete\n", ctrl->index);
}
/**
* dsi_ctrl_hw_14_wait_for_lane_idle()
* This function waits for all the active DSI lanes to be idle by polling all
* the FIFO_EMPTY bits and polling he lane status to ensure that all the lanes
* are in stop state. This function assumes that the bus clocks required to
* access the registers are already turned on.
*
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to be stopped.
*
* return: Error code.
*/
int dsi_ctrl_hw_14_wait_for_lane_idle(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
int rc = 0, val = 0;
u32 stop_state_mask = 0, fifo_empty_mask = 0;
u32 const sleep_us = 10;
u32 const timeout_us = 100;
if (lanes & DSI_DATA_LANE_0) {
stop_state_mask |= BIT(0);
fifo_empty_mask |= (BIT(12) | BIT(16));
}
if (lanes & DSI_DATA_LANE_1) {
stop_state_mask |= BIT(1);
fifo_empty_mask |= BIT(20);
}
if (lanes & DSI_DATA_LANE_2) {
stop_state_mask |= BIT(2);
fifo_empty_mask |= BIT(24);
}
if (lanes & DSI_DATA_LANE_3) {
stop_state_mask |= BIT(3);
fifo_empty_mask |= BIT(28);
}
pr_debug("%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) {
pr_err("%s: fifo not empty, FIFO_STATUS=0x%08x\n",
__func__, val);
goto error;
}
pr_debug("%s: polling for lanes to be in stop state, mask=0x%08x\n",
__func__, stop_state_mask);
rc = readl_poll_timeout(ctrl->base + DSI_LANE_STATUS, val,
(val & stop_state_mask), sleep_us, timeout_us);
if (rc) {
pr_err("%s: lanes not in stop state, LANE_STATUS=0x%08x\n",
__func__, val);
goto error;
}
error:
return rc;
}
/**
* ulps_request() - request ulps entry for specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to enter ULPS.
*
* Caller should check if lanes are in ULPS mode by calling
* get_lanes_in_ulps() operation.
*/
void dsi_ctrl_hw_14_ulps_request(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
u32 reg = 0;
if (lanes & DSI_CLOCK_LANE)
reg = BIT(4);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(0);
if (lanes & DSI_DATA_LANE_1)
reg |= BIT(1);
if (lanes & DSI_DATA_LANE_2)
reg |= BIT(2);
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(3);
/*
* ULPS entry request. Wait for short time to make sure
* that the lanes enter ULPS. Recommended as per HPG.
*/
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
usleep_range(100, 110);
pr_debug("[DSI_%d] ULPS requested for lanes 0x%x\n", ctrl->index,
lanes);
}
/**
* ulps_exit() - exit ULPS on specified lanes
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes (enum dsi_data_lanes) which need
* to exit ULPS.
*
* Caller should check if lanes are in active mode by calling
* get_lanes_in_ulps() operation.
*/
void dsi_ctrl_hw_14_ulps_exit(struct dsi_ctrl_hw *ctrl, u32 lanes)
{
u32 reg = 0;
if (lanes & DSI_CLOCK_LANE)
reg = BIT(12);
if (lanes & DSI_DATA_LANE_0)
reg |= BIT(8);
if (lanes & DSI_DATA_LANE_1)
reg |= BIT(9);
if (lanes & DSI_DATA_LANE_2)
reg |= BIT(10);
if (lanes & DSI_DATA_LANE_3)
reg |= BIT(11);
/*
* ULPS Exit Request
* Hardware requirement is to wait for at least 1ms
*/
DSI_W32(ctrl, DSI_LANE_CTRL, reg);
usleep_range(1000, 1010);
/*
* Sometimes when exiting ULPS, it is possible that some DSI
* lanes are not in the stop state which could lead to DSI
* commands not going through. To avoid this, force the lanes
* to be in stop state.
*/
DSI_W32(ctrl, DSI_LANE_CTRL, reg << 8);
DSI_W32(ctrl, DSI_LANE_CTRL, 0x0);
pr_debug("[DSI_%d] ULPS exit request for lanes=0x%x\n",
ctrl->index, lanes);
}
/**
* get_lanes_in_ulps() - returns the list of lanes in ULPS mode
* @ctrl: Pointer to the controller host hardware.
*
* Returns an ORed list of lanes (enum dsi_data_lanes) that are in ULPS
* state. If 0 is returned, all the lanes are active.
*
* Return: List of lanes in ULPS state.
*/
u32 dsi_ctrl_hw_14_get_lanes_in_ulps(struct dsi_ctrl_hw *ctrl)
{
u32 reg = 0;
u32 lanes = 0;
reg = DSI_R32(ctrl, DSI_LANE_STATUS);
if (!(reg & BIT(8)))
lanes |= DSI_DATA_LANE_0;
if (!(reg & BIT(9)))
lanes |= DSI_DATA_LANE_1;
if (!(reg & BIT(10)))
lanes |= DSI_DATA_LANE_2;
if (!(reg & BIT(11)))
lanes |= DSI_DATA_LANE_3;
if (!(reg & BIT(12)))
lanes |= DSI_CLOCK_LANE;
pr_debug("[DSI_%d] lanes in ulps = 0x%x\n", ctrl->index, lanes);
return lanes;
}
/**
* clamp_enable() - enable DSI clamps to keep PHY driving a stable link
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to be clamped.
* @enable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_enable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool enable_ulps)
{
u32 clamp_reg = 0;
u32 bit_shift = 0;
u32 reg = 0;
if (ctrl->index == 1)
bit_shift = 16;
if (lanes & DSI_CLOCK_LANE) {
clamp_reg |= BIT(9);
if (enable_ulps)
clamp_reg |= BIT(8);
}
if (lanes & DSI_DATA_LANE_0) {
clamp_reg |= BIT(7);
if (enable_ulps)
clamp_reg |= BIT(6);
}
if (lanes & DSI_DATA_LANE_1) {
clamp_reg |= BIT(5);
if (enable_ulps)
clamp_reg |= BIT(4);
}
if (lanes & DSI_DATA_LANE_2) {
clamp_reg |= BIT(3);
if (enable_ulps)
clamp_reg |= BIT(2);
}
if (lanes & DSI_DATA_LANE_3) {
clamp_reg |= BIT(1);
if (enable_ulps)
clamp_reg |= BIT(0);
}
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (clamp_reg << bit_shift);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg |= (BIT(15) << bit_shift); /* Enable clamp */
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
pr_debug("[DSI_%d] Clamps enabled for lanes=0x%x\n", ctrl->index,
lanes);
}
/**
* clamp_disable() - disable DSI clamps
* @ctrl: Pointer to the controller host hardware.
* @lanes: ORed list of lanes which need to have clamps released.
* @disable_ulps: Boolean to specify if ULPS is enabled in DSI controller
*/
void dsi_ctrl_hw_14_clamp_disable(struct dsi_ctrl_hw *ctrl,
u32 lanes,
bool disable_ulps)
{
u32 clamp_reg = 0;
u32 bit_shift = 0;
u32 reg = 0;
if (ctrl->index == 1)
bit_shift = 16;
if (lanes & DSI_CLOCK_LANE) {
clamp_reg |= BIT(9);
if (disable_ulps)
clamp_reg |= BIT(8);
}
if (lanes & DSI_DATA_LANE_0) {
clamp_reg |= BIT(7);
if (disable_ulps)
clamp_reg |= BIT(6);
}
if (lanes & DSI_DATA_LANE_1) {
clamp_reg |= BIT(5);
if (disable_ulps)
clamp_reg |= BIT(4);
}
if (lanes & DSI_DATA_LANE_2) {
clamp_reg |= BIT(3);
if (disable_ulps)
clamp_reg |= BIT(2);
}
if (lanes & DSI_DATA_LANE_3) {
clamp_reg |= BIT(1);
if (disable_ulps)
clamp_reg |= BIT(0);
}
clamp_reg |= BIT(15); /* Enable clamp */
clamp_reg <<= bit_shift;
reg = DSI_MMSS_MISC_R32(ctrl, MMSS_MISC_CLAMP_REG_OFF);
reg &= ~(clamp_reg);
DSI_MMSS_MISC_W32(ctrl, MMSS_MISC_CLAMP_REG_OFF, reg);
pr_debug("[DSI_%d] Disable clamps for lanes=%d\n", ctrl->index, lanes);
}
#define DUMP_REG_VALUE(off) "\t%-30s: 0x%08x\n", #off, DSI_R32(ctrl, off)
ssize_t dsi_ctrl_hw_14_reg_dump_to_buffer(struct dsi_ctrl_hw *ctrl,
char *buf,
u32 size)
{
u32 len = 0;
len += snprintf((buf + len), (size - len), "CONFIGURATION REGS:\n");
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HW_VERSION));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_SYNC_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_PIXEL_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_BLANKING_DATATYPE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_H));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_ACTIVE_V));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_HSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VIDEO_MODE_VSYNC_VPOS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_DMA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_DCS_CMD_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_OFFSET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_CMD_LENGTH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_FIFO_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DMA_NULL_PACKET_DATA));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM0_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM1_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ACK_ERR_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA3));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATATYPE1));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TRIG_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EXT_MUX_TE_PULSE_DETECT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_DMA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_MDP_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CMD_MODE_BTA_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RESET_SW_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LANE_SWAP_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DLN0_PHY_ERR));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_LP_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_HS_TIMER_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TIMEOUT_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLKOUT_TIMING_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_EOT_PACKET_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_GENERIC_ESC_TX_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_ERR_INT_MASK0));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_INT_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_SOFT_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_CLK_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_PHY_SW_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AXI2AHB_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_CTRL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_COMMAND_MODE_MDP_STREAM2_TOTAL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VBIF_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_AES_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_RDBK_DATA_CTRL));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TEST_PATTERN_GEN_CMD_DMA_INIT_VAL2));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_STATUS));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_WRITE_TRIGGER));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_FLUSH));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_DSI_TIMING_DB_MODE));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_TPG_DMA_FIFO_RESET));
len += snprintf((buf + len), (size - len),
DUMP_REG_VALUE(DSI_VERSION));
pr_err("LLENGTH = %d\n", len);
return len;
}