Brooklyn/drivers/gpu/drm/msm/dp/dp_catalog.c
Raziel K. Crowe 04c1822c0a There is a moose on the mool buff
Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey! Ring the door. Take your seat moosey!
2022-03-15 21:13:23 +05:00

1069 lines
30 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
*/
#define pr_fmt(fmt) "[drm-dp] %s: " fmt, __func__
#include <linux/delay.h>
#include <linux/iopoll.h>
#include <linux/phy/phy.h>
#include <linux/phy/phy-dp.h>
#include <linux/rational.h>
#include <drm/drm_dp_helper.h>
#include <drm/drm_print.h>
#include "dp_catalog.h"
#include "dp_reg.h"
#define POLLING_SLEEP_US 1000
#define POLLING_TIMEOUT_US 10000
#define SCRAMBLER_RESET_COUNT_VALUE 0xFC
#define DP_INTERRUPT_STATUS_ACK_SHIFT 1
#define DP_INTERRUPT_STATUS_MASK_SHIFT 2
#define MSM_DP_CONTROLLER_AHB_OFFSET 0x0000
#define MSM_DP_CONTROLLER_AHB_SIZE 0x0200
#define MSM_DP_CONTROLLER_AUX_OFFSET 0x0200
#define MSM_DP_CONTROLLER_AUX_SIZE 0x0200
#define MSM_DP_CONTROLLER_LINK_OFFSET 0x0400
#define MSM_DP_CONTROLLER_LINK_SIZE 0x0C00
#define MSM_DP_CONTROLLER_P0_OFFSET 0x1000
#define MSM_DP_CONTROLLER_P0_SIZE 0x0400
#define DP_INTERRUPT_STATUS1 \
(DP_INTR_AUX_I2C_DONE| \
DP_INTR_WRONG_ADDR | DP_INTR_TIMEOUT | \
DP_INTR_NACK_DEFER | DP_INTR_WRONG_DATA_CNT | \
DP_INTR_I2C_NACK | DP_INTR_I2C_DEFER | \
DP_INTR_PLL_UNLOCKED | DP_INTR_AUX_ERROR)
#define DP_INTERRUPT_STATUS1_ACK \
(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_ACK_SHIFT)
#define DP_INTERRUPT_STATUS1_MASK \
(DP_INTERRUPT_STATUS1 << DP_INTERRUPT_STATUS_MASK_SHIFT)
#define DP_INTERRUPT_STATUS2 \
(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED)
#define DP_INTERRUPT_STATUS2_ACK \
(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_ACK_SHIFT)
#define DP_INTERRUPT_STATUS2_MASK \
(DP_INTERRUPT_STATUS2 << DP_INTERRUPT_STATUS_MASK_SHIFT)
struct dp_catalog_private {
struct device *dev;
struct dp_io *io;
u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_catalog dp_catalog;
u8 aux_lut_cfg_index[PHY_AUX_CFG_MAX];
};
void dp_catalog_snapshot(struct dp_catalog *dp_catalog, struct msm_disp_state *disp_state)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
msm_disp_snapshot_add_block(disp_state, catalog->io->dp_controller.len,
catalog->io->dp_controller.base, "dp_ctrl");
}
static inline u32 dp_read_aux(struct dp_catalog_private *catalog, u32 offset)
{
offset += MSM_DP_CONTROLLER_AUX_OFFSET;
return readl_relaxed(catalog->io->dp_controller.base + offset);
}
static inline void dp_write_aux(struct dp_catalog_private *catalog,
u32 offset, u32 data)
{
offset += MSM_DP_CONTROLLER_AUX_OFFSET;
/*
* To make sure aux reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
writel(data, catalog->io->dp_controller.base + offset);
}
static inline u32 dp_read_ahb(struct dp_catalog_private *catalog, u32 offset)
{
offset += MSM_DP_CONTROLLER_AHB_OFFSET;
return readl_relaxed(catalog->io->dp_controller.base + offset);
}
static inline void dp_write_ahb(struct dp_catalog_private *catalog,
u32 offset, u32 data)
{
offset += MSM_DP_CONTROLLER_AHB_OFFSET;
/*
* To make sure phy reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
writel(data, catalog->io->dp_controller.base + offset);
}
static inline void dp_write_p0(struct dp_catalog_private *catalog,
u32 offset, u32 data)
{
offset += MSM_DP_CONTROLLER_P0_OFFSET;
/*
* To make sure interface reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
writel(data, catalog->io->dp_controller.base + offset);
}
static inline u32 dp_read_p0(struct dp_catalog_private *catalog,
u32 offset)
{
offset += MSM_DP_CONTROLLER_P0_OFFSET;
/*
* To make sure interface reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
return readl_relaxed(catalog->io->dp_controller.base + offset);
}
static inline u32 dp_read_link(struct dp_catalog_private *catalog, u32 offset)
{
offset += MSM_DP_CONTROLLER_LINK_OFFSET;
return readl_relaxed(catalog->io->dp_controller.base + offset);
}
static inline void dp_write_link(struct dp_catalog_private *catalog,
u32 offset, u32 data)
{
offset += MSM_DP_CONTROLLER_LINK_OFFSET;
/*
* To make sure link reg writes happens before any other operation,
* this function uses writel() instread of writel_relaxed()
*/
writel(data, catalog->io->dp_controller.base + offset);
}
/* aux related catalog functions */
u32 dp_catalog_aux_read_data(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
return dp_read_aux(catalog, REG_DP_AUX_DATA);
}
int dp_catalog_aux_write_data(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_aux(catalog, REG_DP_AUX_DATA, dp_catalog->aux_data);
return 0;
}
int dp_catalog_aux_write_trans(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, dp_catalog->aux_data);
return 0;
}
int dp_catalog_aux_clear_trans(struct dp_catalog *dp_catalog, bool read)
{
u32 data;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
if (read) {
data = dp_read_aux(catalog, REG_DP_AUX_TRANS_CTRL);
data &= ~DP_AUX_TRANS_CTRL_GO;
dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, data);
} else {
dp_write_aux(catalog, REG_DP_AUX_TRANS_CTRL, 0);
}
return 0;
}
int dp_catalog_aux_clear_hw_interrupts(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_read_aux(catalog, REG_DP_PHY_AUX_INTERRUPT_STATUS);
dp_write_aux(catalog, REG_DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
dp_write_aux(catalog, REG_DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
dp_write_aux(catalog, REG_DP_PHY_AUX_INTERRUPT_CLEAR, 0);
return 0;
}
/**
* dp_catalog_aux_reset() - reset AUX controller
*
* @dp_catalog: DP catalog structure
*
* return: void
*
* This function reset AUX controller
*
* NOTE: reset AUX controller will also clear any pending HPD related interrupts
*
*/
void dp_catalog_aux_reset(struct dp_catalog *dp_catalog)
{
u32 aux_ctrl;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
aux_ctrl = dp_read_aux(catalog, REG_DP_AUX_CTRL);
aux_ctrl |= DP_AUX_CTRL_RESET;
dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
usleep_range(1000, 1100); /* h/w recommended delay */
aux_ctrl &= ~DP_AUX_CTRL_RESET;
dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
}
void dp_catalog_aux_enable(struct dp_catalog *dp_catalog, bool enable)
{
u32 aux_ctrl;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
aux_ctrl = dp_read_aux(catalog, REG_DP_AUX_CTRL);
if (enable) {
dp_write_aux(catalog, REG_DP_TIMEOUT_COUNT, 0xffff);
dp_write_aux(catalog, REG_DP_AUX_LIMITS, 0xffff);
aux_ctrl |= DP_AUX_CTRL_ENABLE;
} else {
aux_ctrl &= ~DP_AUX_CTRL_ENABLE;
}
dp_write_aux(catalog, REG_DP_AUX_CTRL, aux_ctrl);
}
void dp_catalog_aux_update_cfg(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
struct dp_io *dp_io = catalog->io;
struct phy *phy = dp_io->phy;
phy_calibrate(phy);
}
static void dump_regs(void __iomem *base, int len)
{
int i;
u32 x0, x4, x8, xc;
u32 addr_off = 0;
len = DIV_ROUND_UP(len, 16);
for (i = 0; i < len; i++) {
x0 = readl_relaxed(base + addr_off);
x4 = readl_relaxed(base + addr_off + 0x04);
x8 = readl_relaxed(base + addr_off + 0x08);
xc = readl_relaxed(base + addr_off + 0x0c);
pr_info("%08x: %08x %08x %08x %08x", addr_off, x0, x4, x8, xc);
addr_off += 16;
}
}
void dp_catalog_dump_regs(struct dp_catalog *dp_catalog)
{
u32 offset, len;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
pr_info("AHB regs\n");
offset = MSM_DP_CONTROLLER_AHB_OFFSET;
len = MSM_DP_CONTROLLER_AHB_SIZE;
dump_regs(catalog->io->dp_controller.base + offset, len);
pr_info("AUXCLK regs\n");
offset = MSM_DP_CONTROLLER_AUX_OFFSET;
len = MSM_DP_CONTROLLER_AUX_SIZE;
dump_regs(catalog->io->dp_controller.base + offset, len);
pr_info("LCLK regs\n");
offset = MSM_DP_CONTROLLER_LINK_OFFSET;
len = MSM_DP_CONTROLLER_LINK_SIZE;
dump_regs(catalog->io->dp_controller.base + offset, len);
pr_info("P0CLK regs\n");
offset = MSM_DP_CONTROLLER_P0_OFFSET;
len = MSM_DP_CONTROLLER_P0_SIZE;
dump_regs(catalog->io->dp_controller.base + offset, len);
}
u32 dp_catalog_aux_get_irq(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 intr, intr_ack;
intr = dp_read_ahb(catalog, REG_DP_INTR_STATUS);
intr &= ~DP_INTERRUPT_STATUS1_MASK;
intr_ack = (intr & DP_INTERRUPT_STATUS1)
<< DP_INTERRUPT_STATUS_ACK_SHIFT;
dp_write_ahb(catalog, REG_DP_INTR_STATUS, intr_ack |
DP_INTERRUPT_STATUS1_MASK);
return intr;
}
/* controller related catalog functions */
void dp_catalog_ctrl_update_transfer_unit(struct dp_catalog *dp_catalog,
u32 dp_tu, u32 valid_boundary,
u32 valid_boundary2)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_link(catalog, REG_DP_VALID_BOUNDARY, valid_boundary);
dp_write_link(catalog, REG_DP_TU, dp_tu);
dp_write_link(catalog, REG_DP_VALID_BOUNDARY_2, valid_boundary2);
}
void dp_catalog_ctrl_state_ctrl(struct dp_catalog *dp_catalog, u32 state)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_link(catalog, REG_DP_STATE_CTRL, state);
}
void dp_catalog_ctrl_config_ctrl(struct dp_catalog *dp_catalog, u32 cfg)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
DRM_DEBUG_DP("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
dp_write_link(catalog, REG_DP_CONFIGURATION_CTRL, cfg);
}
void dp_catalog_ctrl_lane_mapping(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 ln_0 = 0, ln_1 = 1, ln_2 = 2, ln_3 = 3; /* One-to-One mapping */
u32 ln_mapping;
ln_mapping = ln_0 << LANE0_MAPPING_SHIFT;
ln_mapping |= ln_1 << LANE1_MAPPING_SHIFT;
ln_mapping |= ln_2 << LANE2_MAPPING_SHIFT;
ln_mapping |= ln_3 << LANE3_MAPPING_SHIFT;
dp_write_link(catalog, REG_DP_LOGICAL2PHYSICAL_LANE_MAPPING,
ln_mapping);
}
void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog *dp_catalog,
bool enable)
{
u32 mainlink_ctrl;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
DRM_DEBUG_DP("enable=%d\n", enable);
if (enable) {
/*
* To make sure link reg writes happens before other operation,
* dp_write_link() function uses writel()
*/
mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
mainlink_ctrl &= ~(DP_MAINLINK_CTRL_RESET |
DP_MAINLINK_CTRL_ENABLE);
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
mainlink_ctrl |= DP_MAINLINK_CTRL_RESET;
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
mainlink_ctrl &= ~DP_MAINLINK_CTRL_RESET;
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
mainlink_ctrl |= (DP_MAINLINK_CTRL_ENABLE |
DP_MAINLINK_FB_BOUNDARY_SEL);
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
} else {
mainlink_ctrl = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
mainlink_ctrl &= ~DP_MAINLINK_CTRL_ENABLE;
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, mainlink_ctrl);
}
}
void dp_catalog_ctrl_config_misc(struct dp_catalog *dp_catalog,
u32 colorimetry_cfg,
u32 test_bits_depth)
{
u32 misc_val;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
misc_val = dp_read_link(catalog, REG_DP_MISC1_MISC0);
/* clear bpp bits */
misc_val &= ~(0x07 << DP_MISC0_TEST_BITS_DEPTH_SHIFT);
misc_val |= colorimetry_cfg << DP_MISC0_COLORIMETRY_CFG_SHIFT;
misc_val |= test_bits_depth << DP_MISC0_TEST_BITS_DEPTH_SHIFT;
/* Configure clock to synchronous mode */
misc_val |= DP_MISC0_SYNCHRONOUS_CLK;
DRM_DEBUG_DP("misc settings = 0x%x\n", misc_val);
dp_write_link(catalog, REG_DP_MISC1_MISC0, misc_val);
}
void dp_catalog_ctrl_config_msa(struct dp_catalog *dp_catalog,
u32 rate, u32 stream_rate_khz,
bool fixed_nvid)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid, pixel_div = 0, dispcc_input_rate;
u32 const nvid_fixed = DP_LINK_CONSTANT_N_VALUE;
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
unsigned long den, num;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
if (rate == link_rate_hbr3)
pixel_div = 6;
else if (rate == 1620000 || rate == 270000)
pixel_div = 2;
else if (rate == link_rate_hbr2)
pixel_div = 4;
else
DRM_ERROR("Invalid pixel mux divider\n");
dispcc_input_rate = (rate * 10) / pixel_div;
rational_best_approximation(dispcc_input_rate, stream_rate_khz,
(unsigned long)(1 << 16) - 1,
(unsigned long)(1 << 16) - 1, &den, &num);
den = ~(den - num);
den = den & 0xFFFF;
pixel_m = num;
pixel_n = den;
mvid = (pixel_m & 0xFFFF) * 5;
nvid = (0xFFFF & (~pixel_n)) + (pixel_m & 0xFFFF);
if (nvid < nvid_fixed) {
u32 temp;
temp = (nvid_fixed / nvid) * nvid;
mvid = (nvid_fixed / nvid) * mvid;
nvid = temp;
}
if (link_rate_hbr2 == rate)
nvid *= 2;
if (link_rate_hbr3 == rate)
nvid *= 3;
DRM_DEBUG_DP("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write_link(catalog, REG_DP_SOFTWARE_MVID, mvid);
dp_write_link(catalog, REG_DP_SOFTWARE_NVID, nvid);
dp_write_p0(catalog, MMSS_DP_DSC_DTO, 0x0);
}
int dp_catalog_ctrl_set_pattern(struct dp_catalog *dp_catalog,
u32 pattern)
{
int bit, ret;
u32 data;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
bit = BIT(pattern - 1);
DRM_DEBUG_DP("hw: bit=%d train=%d\n", bit, pattern);
dp_catalog_ctrl_state_ctrl(dp_catalog, bit);
bit = BIT(pattern - 1) << DP_MAINLINK_READY_LINK_TRAINING_SHIFT;
/* Poll for mainlink ready status */
ret = readx_poll_timeout(readl, catalog->io->dp_controller.base +
MSM_DP_CONTROLLER_LINK_OFFSET +
REG_DP_MAINLINK_READY,
data, data & bit,
POLLING_SLEEP_US, POLLING_TIMEOUT_US);
if (ret < 0) {
DRM_ERROR("set pattern for link_train=%d failed\n", pattern);
return ret;
}
return 0;
}
/**
* dp_catalog_ctrl_reset() - reset DP controller
*
* @dp_catalog: DP catalog structure
*
* return: void
*
* This function reset the DP controller
*
* NOTE: reset DP controller will also clear any pending HPD related interrupts
*
*/
void dp_catalog_ctrl_reset(struct dp_catalog *dp_catalog)
{
u32 sw_reset;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
sw_reset = dp_read_ahb(catalog, REG_DP_SW_RESET);
sw_reset |= DP_SW_RESET;
dp_write_ahb(catalog, REG_DP_SW_RESET, sw_reset);
usleep_range(1000, 1100); /* h/w recommended delay */
sw_reset &= ~DP_SW_RESET;
dp_write_ahb(catalog, REG_DP_SW_RESET, sw_reset);
}
bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog *dp_catalog)
{
u32 data;
int ret;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
/* Poll for mainlink ready status */
ret = readl_poll_timeout(catalog->io->dp_controller.base +
MSM_DP_CONTROLLER_LINK_OFFSET +
REG_DP_MAINLINK_READY,
data, data & DP_MAINLINK_READY_FOR_VIDEO,
POLLING_SLEEP_US, POLLING_TIMEOUT_US);
if (ret < 0) {
DRM_ERROR("mainlink not ready\n");
return false;
}
return true;
}
void dp_catalog_ctrl_enable_irq(struct dp_catalog *dp_catalog,
bool enable)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
if (enable) {
dp_write_ahb(catalog, REG_DP_INTR_STATUS,
DP_INTERRUPT_STATUS1_MASK);
dp_write_ahb(catalog, REG_DP_INTR_STATUS2,
DP_INTERRUPT_STATUS2_MASK);
} else {
dp_write_ahb(catalog, REG_DP_INTR_STATUS, 0x00);
dp_write_ahb(catalog, REG_DP_INTR_STATUS2, 0x00);
}
}
void dp_catalog_hpd_config_intr(struct dp_catalog *dp_catalog,
u32 intr_mask, bool en)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 config = dp_read_aux(catalog, REG_DP_DP_HPD_INT_MASK);
config = (en ? config | intr_mask : config & ~intr_mask);
DRM_DEBUG_DP("intr_mask=%#x config=%#x\n", intr_mask, config);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_MASK,
config & DP_DP_HPD_INT_MASK);
}
void dp_catalog_ctrl_hpd_config(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 reftimer = dp_read_aux(catalog, REG_DP_DP_HPD_REFTIMER);
/* enable HPD plug and unplug interrupts */
dp_catalog_hpd_config_intr(dp_catalog,
DP_DP_HPD_PLUG_INT_MASK | DP_DP_HPD_UNPLUG_INT_MASK, true);
/* Configure REFTIMER and enable it */
reftimer |= DP_DP_HPD_REFTIMER_ENABLE;
dp_write_aux(catalog, REG_DP_DP_HPD_REFTIMER, reftimer);
/* Enable HPD */
dp_write_aux(catalog, REG_DP_DP_HPD_CTRL, DP_DP_HPD_CTRL_HPD_EN);
}
u32 dp_catalog_link_is_connected(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 status;
status = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
DRM_DEBUG_DP("aux status: %#x\n", status);
status >>= DP_DP_HPD_STATE_STATUS_BITS_SHIFT;
status &= DP_DP_HPD_STATE_STATUS_BITS_MASK;
return status;
}
u32 dp_catalog_hpd_get_intr_status(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
int isr = 0;
isr = dp_read_aux(catalog, REG_DP_DP_HPD_INT_STATUS);
dp_write_aux(catalog, REG_DP_DP_HPD_INT_ACK,
(isr & DP_DP_HPD_INT_MASK));
return isr;
}
int dp_catalog_ctrl_get_interrupt(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 intr, intr_ack;
intr = dp_read_ahb(catalog, REG_DP_INTR_STATUS2);
intr &= ~DP_INTERRUPT_STATUS2_MASK;
intr_ack = (intr & DP_INTERRUPT_STATUS2)
<< DP_INTERRUPT_STATUS_ACK_SHIFT;
dp_write_ahb(catalog, REG_DP_INTR_STATUS2,
intr_ack | DP_INTERRUPT_STATUS2_MASK);
return intr;
}
void dp_catalog_ctrl_phy_reset(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_ahb(catalog, REG_DP_PHY_CTRL,
DP_PHY_CTRL_SW_RESET | DP_PHY_CTRL_SW_RESET_PLL);
usleep_range(1000, 1100); /* h/w recommended delay */
dp_write_ahb(catalog, REG_DP_PHY_CTRL, 0x0);
}
int dp_catalog_ctrl_update_vx_px(struct dp_catalog *dp_catalog,
u8 v_level, u8 p_level)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
struct dp_io *dp_io = catalog->io;
struct phy *phy = dp_io->phy;
struct phy_configure_opts_dp *opts_dp = &dp_io->phy_opts.dp;
/* TODO: Update for all lanes instead of just first one */
opts_dp->voltage[0] = v_level;
opts_dp->pre[0] = p_level;
opts_dp->set_voltages = 1;
phy_configure(phy, &dp_io->phy_opts);
opts_dp->set_voltages = 0;
return 0;
}
void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog *dp_catalog,
u32 pattern)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 value = 0x0;
/* Make sure to clear the current pattern before starting a new one */
dp_write_link(catalog, REG_DP_STATE_CTRL, 0x0);
DRM_DEBUG_DP("pattern: %#x\n", pattern);
switch (pattern) {
case DP_PHY_TEST_PATTERN_D10_2:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_TRAINING_PATTERN1);
break;
case DP_PHY_TEST_PATTERN_ERROR_COUNT:
value &= ~(1 << 16);
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
value);
value |= SCRAMBLER_RESET_COUNT_VALUE;
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
value);
dp_write_link(catalog, REG_DP_MAINLINK_LEVELS,
DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2);
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE);
break;
case DP_PHY_TEST_PATTERN_PRBS7:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_PRBS7);
break;
case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_TEST_CUSTOM_PATTERN);
/* 00111110000011111000001111100000 */
dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG0,
0x3E0F83E0);
/* 00001111100000111110000011111000 */
dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG1,
0x0F83E0F8);
/* 1111100000111110 */
dp_write_link(catalog, REG_DP_TEST_80BIT_CUSTOM_PATTERN_REG2,
0x0000F83E);
break;
case DP_PHY_TEST_PATTERN_CP2520:
value = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
value &= ~DP_MAINLINK_CTRL_SW_BYPASS_SCRAMBLER;
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, value);
value = DP_HBR2_ERM_PATTERN;
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
value);
value |= SCRAMBLER_RESET_COUNT_VALUE;
dp_write_link(catalog, REG_DP_HBR2_COMPLIANCE_SCRAMBLER_RESET,
value);
dp_write_link(catalog, REG_DP_MAINLINK_LEVELS,
DP_MAINLINK_SAFE_TO_EXIT_LEVEL_2);
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_SYMBOL_ERR_MEASURE);
value = dp_read_link(catalog, REG_DP_MAINLINK_CTRL);
value |= DP_MAINLINK_CTRL_ENABLE;
dp_write_link(catalog, REG_DP_MAINLINK_CTRL, value);
break;
case DP_PHY_TEST_PATTERN_SEL_MASK:
dp_write_link(catalog, REG_DP_MAINLINK_CTRL,
DP_MAINLINK_CTRL_ENABLE);
dp_write_link(catalog, REG_DP_STATE_CTRL,
DP_STATE_CTRL_LINK_TRAINING_PATTERN4);
break;
default:
DRM_DEBUG_DP("No valid test pattern requested: %#x\n", pattern);
break;
}
}
u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
return dp_read_link(catalog, REG_DP_MAINLINK_READY);
}
/* panel related catalog functions */
int dp_catalog_panel_timing_cfg(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_link(catalog, REG_DP_TOTAL_HOR_VER,
dp_catalog->total);
dp_write_link(catalog, REG_DP_START_HOR_VER_FROM_SYNC,
dp_catalog->sync_start);
dp_write_link(catalog, REG_DP_HSYNC_VSYNC_WIDTH_POLARITY,
dp_catalog->width_blanking);
dp_write_link(catalog, REG_DP_ACTIVE_HOR_VER, dp_catalog->dp_active);
dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0);
return 0;
}
void dp_catalog_panel_tpg_enable(struct dp_catalog *dp_catalog,
struct drm_display_mode *drm_mode)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
u32 hsync_period, vsync_period;
u32 display_v_start, display_v_end;
u32 hsync_start_x, hsync_end_x;
u32 v_sync_width;
u32 hsync_ctl;
u32 display_hctl;
/* TPG config parameters*/
hsync_period = drm_mode->htotal;
vsync_period = drm_mode->vtotal;
display_v_start = ((drm_mode->vtotal - drm_mode->vsync_start) *
hsync_period);
display_v_end = ((vsync_period - (drm_mode->vsync_start -
drm_mode->vdisplay))
* hsync_period) - 1;
display_v_start += drm_mode->htotal - drm_mode->hsync_start;
display_v_end -= (drm_mode->hsync_start - drm_mode->hdisplay);
hsync_start_x = drm_mode->htotal - drm_mode->hsync_start;
hsync_end_x = hsync_period - (drm_mode->hsync_start -
drm_mode->hdisplay) - 1;
v_sync_width = drm_mode->vsync_end - drm_mode->vsync_start;
hsync_ctl = (hsync_period << 16) |
(drm_mode->hsync_end - drm_mode->hsync_start);
display_hctl = (hsync_end_x << 16) | hsync_start_x;
dp_write_p0(catalog, MMSS_DP_INTF_CONFIG, 0x0);
dp_write_p0(catalog, MMSS_DP_INTF_HSYNC_CTL, hsync_ctl);
dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F0, vsync_period *
hsync_period);
dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0, v_sync_width *
hsync_period);
dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_HCTL, display_hctl);
dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_HCTL, 0);
dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F0, display_v_start);
dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F0, display_v_end);
dp_write_p0(catalog, MMSS_INTF_DISPLAY_V_START_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
dp_write_p0(catalog, MMSS_DP_INTF_POLARITY_CTL, 0);
dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL,
DP_TPG_CHECKERED_RECT_PATTERN);
dp_write_p0(catalog, MMSS_DP_TPG_VIDEO_CONFIG,
DP_TPG_VIDEO_CONFIG_BPP_8BIT |
DP_TPG_VIDEO_CONFIG_RGB);
dp_write_p0(catalog, MMSS_DP_BIST_ENABLE,
DP_BIST_ENABLE_DPBIST_EN);
dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN,
DP_TIMING_ENGINE_EN_EN);
DRM_DEBUG_DP("%s: enabled tpg\n", __func__);
}
void dp_catalog_panel_tpg_disable(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_p0(catalog, MMSS_DP_TPG_MAIN_CONTROL, 0x0);
dp_write_p0(catalog, MMSS_DP_BIST_ENABLE, 0x0);
dp_write_p0(catalog, MMSS_DP_TIMING_ENGINE_EN, 0x0);
}
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_io *io)
{
struct dp_catalog_private *catalog;
if (!io) {
DRM_ERROR("invalid input\n");
return ERR_PTR(-EINVAL);
}
catalog = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL);
if (!catalog)
return ERR_PTR(-ENOMEM);
catalog->dev = dev;
catalog->io = io;
return &catalog->dp_catalog;
}
void dp_catalog_audio_get_header(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
sdp_map = catalog->audio_map;
sdp = dp_catalog->sdp_type;
header = dp_catalog->sdp_header;
dp_catalog->audio_data = dp_read_link(catalog,
sdp_map[sdp][header]);
}
void dp_catalog_audio_set_header(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
u32 data;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
sdp_map = catalog->audio_map;
sdp = dp_catalog->sdp_type;
header = dp_catalog->sdp_header;
data = dp_catalog->audio_data;
dp_write_link(catalog, sdp_map[sdp][header], data);
}
void dp_catalog_audio_config_acr(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
u32 acr_ctrl, select;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
select = dp_catalog->audio_data;
acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
DRM_DEBUG_DP("select: %#x, acr_ctrl: %#x\n", select, acr_ctrl);
dp_write_link(catalog, MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
}
void dp_catalog_audio_enable(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
bool enable;
u32 audio_ctrl;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
enable = !!dp_catalog->audio_data;
audio_ctrl = dp_read_link(catalog, MMSS_DP_AUDIO_CFG);
if (enable)
audio_ctrl |= BIT(0);
else
audio_ctrl &= ~BIT(0);
DRM_DEBUG_DP("dp_audio_cfg = 0x%x\n", audio_ctrl);
dp_write_link(catalog, MMSS_DP_AUDIO_CFG, audio_ctrl);
/* make sure audio engine is disabled */
wmb();
}
void dp_catalog_audio_config_sdp(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
u32 sdp_cfg = 0;
u32 sdp_cfg2 = 0;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
sdp_cfg = dp_read_link(catalog, MMSS_DP_SDP_CFG);
/* AUDIO_TIMESTAMP_SDP_EN */
sdp_cfg |= BIT(1);
/* AUDIO_STREAM_SDP_EN */
sdp_cfg |= BIT(2);
/* AUDIO_COPY_MANAGEMENT_SDP_EN */
sdp_cfg |= BIT(5);
/* AUDIO_ISRC_SDP_EN */
sdp_cfg |= BIT(6);
/* AUDIO_INFOFRAME_SDP_EN */
sdp_cfg |= BIT(20);
DRM_DEBUG_DP("sdp_cfg = 0x%x\n", sdp_cfg);
dp_write_link(catalog, MMSS_DP_SDP_CFG, sdp_cfg);
sdp_cfg2 = dp_read_link(catalog, MMSS_DP_SDP_CFG2);
/* IFRM_REGSRC -> Do not use reg values */
sdp_cfg2 &= ~BIT(0);
/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
sdp_cfg2 &= ~BIT(1);
DRM_DEBUG_DP("sdp_cfg2 = 0x%x\n", sdp_cfg2);
dp_write_link(catalog, MMSS_DP_SDP_CFG2, sdp_cfg2);
}
void dp_catalog_audio_init(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
static u32 sdp_map[][DP_AUDIO_SDP_HEADER_MAX] = {
{
MMSS_DP_AUDIO_STREAM_0,
MMSS_DP_AUDIO_STREAM_1,
MMSS_DP_AUDIO_STREAM_1,
},
{
MMSS_DP_AUDIO_TIMESTAMP_0,
MMSS_DP_AUDIO_TIMESTAMP_1,
MMSS_DP_AUDIO_TIMESTAMP_1,
},
{
MMSS_DP_AUDIO_INFOFRAME_0,
MMSS_DP_AUDIO_INFOFRAME_1,
MMSS_DP_AUDIO_INFOFRAME_1,
},
{
MMSS_DP_AUDIO_COPYMANAGEMENT_0,
MMSS_DP_AUDIO_COPYMANAGEMENT_1,
MMSS_DP_AUDIO_COPYMANAGEMENT_1,
},
{
MMSS_DP_AUDIO_ISRC_0,
MMSS_DP_AUDIO_ISRC_1,
MMSS_DP_AUDIO_ISRC_1,
},
};
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
catalog->audio_map = sdp_map;
}
void dp_catalog_audio_sfe_level(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
u32 mainlink_levels, safe_to_exit_level;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
safe_to_exit_level = dp_catalog->audio_data;
mainlink_levels = dp_read_link(catalog, REG_DP_MAINLINK_LEVELS);
mainlink_levels &= 0xFE0;
mainlink_levels |= safe_to_exit_level;
DRM_DEBUG_DP("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
mainlink_levels, safe_to_exit_level);
dp_write_link(catalog, REG_DP_MAINLINK_LEVELS, mainlink_levels);
}