Files
android_kernel_samsung_sm86…/msm/dp/dp_catalog.c
Rajkumar Subbiah 7eef92843d disp: msm: dp: add debug node to capture source and sink crc
This change adds a debug node named 'crc' to drm_dp to read
the frame CRC values for DP controller and DP Sink. In order
to facilitate the immediate read of the CRC values when
accessed, it enables the CRC calculation on the controller
and sink automatically when the stream is enabled. In addition
to the frame CRC values it also reads the MISR values from
controller and PHY to validate the data flow from controller
to PHY.

Change-Id: I1acee2dba931e4635caf4a400e336a72c86e88bf
Signed-off-by: Rajkumar Subbiah <quic_rsubbia@quicinc.com>
2022-08-24 13:47:27 -07:00

3080 line
74 KiB
C

// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
* Copyright (c) 2017-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/delay.h>
#include <linux/iopoll.h>
#include "dp_catalog.h"
#include "dp_reg.h"
#include "dp_debug.h"
#include "dp_link.h"
#define DP_GET_MSB(x) (x >> 8)
#define DP_GET_LSB(x) (x & 0xff)
#define DP_PHY_READY BIT(1)
#define dp_catalog_get_priv(x) ({ \
struct dp_catalog *dp_catalog; \
dp_catalog = container_of(x, struct dp_catalog, x); \
container_of(dp_catalog, struct dp_catalog_private, \
dp_catalog); \
})
#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_INTR_MASK1 (DP_INTERRUPT_STATUS1 << 2)
#define DP_INTERRUPT_STATUS2 \
(DP_INTR_READY_FOR_VIDEO | DP_INTR_IDLE_PATTERN_SENT | \
DP_INTR_FRAME_END | DP_INTR_CRC_UPDATED)
#define DP_INTR_MASK2 (DP_INTERRUPT_STATUS2 << 2)
#define DP_INTERRUPT_STATUS5 \
(DP_INTR_MST_DP0_VCPF_SENT | DP_INTR_MST_DP1_VCPF_SENT)
#define DP_INTR_MASK5 (DP_INTERRUPT_STATUS5 << 2)
#define DP_TPG_PATTERN_MAX 9
#define DP_TPG_PATTERN_DEFAULT 8
#define dp_catalog_fill_io(x) { \
catalog->io.x = parser->get_io(parser, #x); \
}
#define dp_catalog_fill_io_buf(x) { \
parser->get_io_buf(parser, #x); \
}
#define dp_read(x) ({ \
catalog->read(catalog, io_data, x); \
})
#define dp_write(x, y) ({ \
catalog->write(catalog, io_data, x, y); \
})
static u8 const vm_pre_emphasis[4][4] = {
{0x00, 0x0B, 0x12, 0xFF}, /* pe0, 0 db */
{0x00, 0x0A, 0x12, 0xFF}, /* pe1, 3.5 db */
{0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0xFF, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
};
/* voltage swing, 0.2v and 1.0v are not support */
static u8 const vm_voltage_swing[4][4] = {
{0x07, 0x0F, 0x14, 0xFF}, /* sw0, 0.4v */
{0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */
{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
{0xFF, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};
static u8 const vm_pre_emphasis_hbr3_hbr2[4][4] = {
{0x00, 0x0C, 0x15, 0x1A},
{0x02, 0x0E, 0x16, 0xFF},
{0x02, 0x11, 0xFF, 0xFF},
{0x04, 0xFF, 0xFF, 0xFF}
};
static u8 const vm_voltage_swing_hbr3_hbr2[4][4] = {
{0x02, 0x12, 0x16, 0x1A},
{0x09, 0x19, 0x1F, 0xFF},
{0x10, 0x1F, 0xFF, 0xFF},
{0x1F, 0xFF, 0xFF, 0xFF}
};
static u8 const vm_pre_emphasis_hbr_rbr[4][4] = {
{0x00, 0x0C, 0x14, 0x19},
{0x00, 0x0B, 0x12, 0xFF},
{0x00, 0x0B, 0xFF, 0xFF},
{0x04, 0xFF, 0xFF, 0xFF}
};
static u8 const vm_voltage_swing_hbr_rbr[4][4] = {
{0x08, 0x0F, 0x16, 0x1F},
{0x11, 0x1E, 0x1F, 0xFF},
{0x19, 0x1F, 0xFF, 0xFF},
{0x1F, 0xFF, 0xFF, 0xFF}
};
enum dp_flush_bit {
DP_PPS_FLUSH,
DP_DHDR_FLUSH,
};
/* audio related catalog functions */
struct dp_catalog_private {
struct device *dev;
struct dp_catalog_io io;
struct dp_parser *parser;
u32 (*read)(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset);
void (*write)(struct dp_catalog_private *catlog,
struct dp_io_data *io_data, u32 offset, u32 data);
u32 (*audio_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_catalog dp_catalog;
char exe_mode[SZ_4];
u32 dp_core_version;
u32 dp_phy_version;
};
static u32 dp_read_sw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset)
{
u32 data = 0;
if (io_data->buf)
memcpy(&data, io_data->buf + offset, sizeof(offset));
return data;
}
static void dp_write_sw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
if (io_data->buf)
memcpy(io_data->buf + offset, &data, sizeof(data));
}
static u32 dp_read_hw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset)
{
u32 data = 0;
data = readl_relaxed(io_data->io.base + offset);
return data;
}
static void dp_write_hw(struct dp_catalog_private *catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
writel_relaxed(data, io_data->io.base + offset);
}
static u32 dp_read_sub_sw(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
return dp_read_sw(catalog, io_data, offset);
}
static void dp_write_sub_sw(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_sw(catalog, io_data, offset, data);
}
static u32 dp_read_sub_hw(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
return dp_read_hw(catalog, io_data, offset);
}
static void dp_write_sub_hw(struct dp_catalog *dp_catalog,
struct dp_io_data *io_data, u32 offset, u32 data)
{
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
dp_write_hw(catalog, io_data, offset, data);
}
/* aux related catalog functions */
static u32 dp_catalog_aux_read_data(struct dp_catalog_aux *aux)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
goto end;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
return dp_read(DP_AUX_DATA);
end:
return 0;
}
static int dp_catalog_aux_write_data(struct dp_catalog_aux *aux)
{
int rc = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto end;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
dp_write(DP_AUX_DATA, aux->data);
end:
return rc;
}
static int dp_catalog_aux_write_trans(struct dp_catalog_aux *aux)
{
int rc = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto end;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
dp_write(DP_AUX_TRANS_CTRL, aux->data);
end:
return rc;
}
static int dp_catalog_aux_clear_trans(struct dp_catalog_aux *aux, bool read)
{
int rc = 0;
u32 data = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto end;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
if (read) {
data = dp_read(DP_AUX_TRANS_CTRL);
data &= ~BIT(9);
dp_write(DP_AUX_TRANS_CTRL, data);
} else {
dp_write(DP_AUX_TRANS_CTRL, 0);
}
end:
return rc;
}
static void dp_catalog_aux_clear_hw_interrupts(struct dp_catalog_aux *aux)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 data = 0;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_phy;
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS);
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x1f);
wmb(); /* make sure 0x1f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0x9f);
wmb(); /* make sure 0x9f is written before next write */
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR, 0);
wmb(); /* make sure register is cleared */
}
static void dp_catalog_aux_reset(struct dp_catalog_aux *aux)
{
u32 aux_ctrl;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
aux_ctrl = dp_read(DP_AUX_CTRL);
aux_ctrl |= BIT(1);
dp_write(DP_AUX_CTRL, aux_ctrl);
usleep_range(1000, 1010); /* h/w recommended delay */
aux_ctrl &= ~BIT(1);
dp_write(DP_AUX_CTRL, aux_ctrl);
wmb(); /* make sure AUX reset is done here */
}
static void dp_catalog_aux_enable(struct dp_catalog_aux *aux, bool enable)
{
u32 aux_ctrl;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_aux;
aux_ctrl = dp_read(DP_AUX_CTRL);
if (enable) {
aux_ctrl |= BIT(0);
dp_write(DP_AUX_CTRL, aux_ctrl);
wmb(); /* make sure AUX module is enabled */
dp_write(DP_TIMEOUT_COUNT, 0xffff);
dp_write(DP_AUX_LIMITS, 0xffff);
} else {
aux_ctrl &= ~BIT(0);
dp_write(DP_AUX_CTRL, aux_ctrl);
}
}
static void dp_catalog_aux_update_cfg(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg, enum dp_phy_aux_config_type type)
{
struct dp_catalog_private *catalog;
u32 new_index = 0, current_index = 0;
struct dp_io_data *io_data;
if (!aux || !cfg || (type >= PHY_AUX_CFG_MAX)) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_phy;
current_index = cfg[type].current_index;
new_index = (current_index + 1) % cfg[type].cfg_cnt;
DP_DEBUG("Updating %s from 0x%08x to 0x%08x\n",
dp_phy_aux_config_type_to_string(type),
cfg[type].lut[current_index], cfg[type].lut[new_index]);
dp_write(cfg[type].offset, cfg[type].lut[new_index]);
cfg[type].current_index = new_index;
}
static void dp_catalog_aux_setup(struct dp_catalog_aux *aux,
struct dp_aux_cfg *cfg)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
int i = 0;
if (!aux || !cfg) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_phy;
dp_write(DP_PHY_PD_CTL, 0x65);
wmb(); /* make sure PD programming happened */
/* Turn on BIAS current for PHY/PLL */
io_data = catalog->io.dp_pll;
dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x1b);
io_data = catalog->io.dp_phy;
dp_write(DP_PHY_PD_CTL, 0x02);
wmb(); /* make sure PD programming happened */
dp_write(DP_PHY_PD_CTL, 0x7d);
/* Turn on BIAS current for PHY/PLL */
io_data = catalog->io.dp_pll;
dp_write(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3f);
/* DP AUX CFG register programming */
io_data = catalog->io.dp_phy;
for (i = 0; i < PHY_AUX_CFG_MAX; i++)
dp_write(cfg[i].offset, cfg[i].lut[cfg[i].current_index]);
dp_write(DP_PHY_AUX_INTERRUPT_MASK, 0x1F);
wmb(); /* make sure AUX configuration is done before enabling it */
}
static void dp_catalog_aux_get_irq(struct dp_catalog_aux *aux, bool cmd_busy)
{
u32 ack;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!aux) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(aux);
io_data = catalog->io.dp_ahb;
aux->isr = dp_read(DP_INTR_STATUS);
aux->isr &= ~DP_INTR_MASK1;
ack = aux->isr & DP_INTERRUPT_STATUS1;
ack <<= 1;
ack |= DP_INTR_MASK1;
dp_write(DP_INTR_STATUS, ack);
}
static bool dp_catalog_ctrl_wait_for_phy_ready(
struct dp_catalog_private *catalog)
{
u32 phy_version;
u32 reg, state;
void __iomem *base = catalog->io.dp_phy->io.base;
bool success = true;
u32 const poll_sleep_us = 500;
u32 const pll_timeout_us = 10000;
phy_version = dp_catalog_get_dp_phy_version(&catalog->dp_catalog);
if (phy_version >= 0x60000000) {
reg = DP_PHY_STATUS_V600;
} else {
reg = DP_PHY_STATUS;
}
if (readl_poll_timeout_atomic((base + reg), state,
((state & DP_PHY_READY) > 0),
poll_sleep_us, pll_timeout_us)) {
DP_ERR("PHY status failed, status=%x\n", state);
success = false;
}
return success;
}
/* controller related catalog functions */
static int dp_catalog_ctrl_late_phy_init(struct dp_catalog_ctrl *ctrl,
u8 lane_cnt, bool flipped)
{
int rc = 0;
u32 bias0_en, drvr0_en, bias1_en, drvr1_en;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return -EINVAL;
}
catalog = dp_catalog_get_priv(ctrl);
switch (lane_cnt) {
case 1:
drvr0_en = flipped ? 0x13 : 0x10;
bias0_en = flipped ? 0x3E : 0x15;
drvr1_en = flipped ? 0x10 : 0x13;
bias1_en = flipped ? 0x15 : 0x3E;
break;
case 2:
drvr0_en = flipped ? 0x10 : 0x10;
bias0_en = flipped ? 0x3F : 0x15;
drvr1_en = flipped ? 0x10 : 0x10;
bias1_en = flipped ? 0x15 : 0x3F;
break;
case 4:
default:
drvr0_en = 0x10;
bias0_en = 0x3F;
drvr1_en = 0x10;
bias1_en = 0x3F;
break;
}
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_HIGHZ_DRVR_EN_V420, drvr0_en);
dp_write(TXn_TRANSCEIVER_BIAS_EN_V420, bias0_en);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_HIGHZ_DRVR_EN_V420, drvr1_en);
dp_write(TXn_TRANSCEIVER_BIAS_EN_V420, bias1_en);
io_data = catalog->io.dp_phy;
dp_write(DP_PHY_CFG, 0x18);
/* add hardware recommended delay */
udelay(2000);
dp_write(DP_PHY_CFG, 0x19);
/*
* Make sure all the register writes are completed before
* doing any other operation
*/
wmb();
if (!dp_catalog_ctrl_wait_for_phy_ready(catalog)) {
rc = -EINVAL;
goto lock_err;
}
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_POL_INV_V420, 0x0a);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_POL_INV_V420, 0x0a);
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL_V420, 0x27);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_DRV_LVL_V420, 0x27);
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
/* Make sure the PHY register writes are done */
wmb();
lock_err:
return rc;
}
static u32 dp_catalog_ctrl_read_hdcp_status(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return -EINVAL;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
return dp_read(DP_HDCP_STATUS);
}
static void dp_catalog_panel_sdp_update(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 sdp_cfg3_off = 0;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
if (panel->stream_id == DP_STREAM_1)
sdp_cfg3_off = MMSS_DP1_SDP_CFG3 - MMSS_DP_SDP_CFG3;
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
dp_write(MMSS_DP_SDP_CFG3 + sdp_cfg3_off, 0x01);
dp_write(MMSS_DP_SDP_CFG3 + sdp_cfg3_off, 0x00);
}
static void dp_catalog_panel_setup_vsif_infoframe_sdp(
struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct drm_msm_ext_hdr_metadata *hdr;
struct dp_io_data *io_data;
u32 header, parity, data, mst_offset = 0;
u8 buf[SZ_64], off = 0;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
if (panel->stream_id == DP_STREAM_1)
mst_offset = MMSS_DP1_VSCEXT_0 - MMSS_DP_VSCEXT_0;
catalog = dp_catalog_get_priv(panel);
hdr = &panel->hdr_meta;
io_data = catalog->io.dp_link;
/* HEADER BYTE 1 */
header = panel->dhdr_vsif_sdp.HB1;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_1_BIT)
| (parity << PARITY_BYTE_1_BIT));
dp_write(MMSS_DP_VSCEXT_0 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
/* HEADER BYTE 2 */
header = panel->dhdr_vsif_sdp.HB2;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_2_BIT)
| (parity << PARITY_BYTE_2_BIT));
dp_write(MMSS_DP_VSCEXT_1 + mst_offset, data);
/* HEADER BYTE 3 */
header = panel->dhdr_vsif_sdp.HB3;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_3_BIT)
| (parity << PARITY_BYTE_3_BIT));
data |= dp_read(MMSS_DP_VSCEXT_1 + mst_offset);
dp_write(MMSS_DP_VSCEXT_1 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
print_hex_dump_debug("[drm-dp] VSCEXT: ",
DUMP_PREFIX_NONE, 16, 4, buf, off, false);
}
static void dp_catalog_panel_setup_hdr_infoframe_sdp(
struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct drm_msm_ext_hdr_metadata *hdr;
struct dp_io_data *io_data;
u32 header, parity, data, mst_offset = 0;
u8 buf[SZ_64], off = 0;
u32 const version = 0x01;
u32 const length = 0x1a;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
if (panel->stream_id == DP_STREAM_1)
mst_offset = MMSS_DP1_GENERIC2_0 - MMSS_DP_GENERIC2_0;
catalog = dp_catalog_get_priv(panel);
hdr = &panel->hdr_meta;
io_data = catalog->io.dp_link;
/* HEADER BYTE 1 */
header = panel->shdr_if_sdp.HB1;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_1_BIT)
| (parity << PARITY_BYTE_1_BIT));
dp_write(MMSS_DP_GENERIC2_0 + mst_offset,
data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
/* HEADER BYTE 2 */
header = panel->shdr_if_sdp.HB2;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_2_BIT)
| (parity << PARITY_BYTE_2_BIT));
dp_write(MMSS_DP_GENERIC2_1 + mst_offset, data);
/* HEADER BYTE 3 */
header = panel->shdr_if_sdp.HB3;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_3_BIT)
| (parity << PARITY_BYTE_3_BIT));
data |= dp_read(MMSS_DP_VSCEXT_1 + mst_offset);
dp_write(MMSS_DP_GENERIC2_1 + mst_offset,
data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = version;
data |= length << 8;
data |= hdr->eotf << 16;
dp_write(MMSS_DP_GENERIC2_2 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->display_primaries_x[0]) |
(DP_GET_MSB(hdr->display_primaries_x[0]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[0]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[0]) << 24));
dp_write(MMSS_DP_GENERIC2_3 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->display_primaries_x[1]) |
(DP_GET_MSB(hdr->display_primaries_x[1]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[1]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[1]) << 24));
dp_write(MMSS_DP_GENERIC2_4 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->display_primaries_x[2]) |
(DP_GET_MSB(hdr->display_primaries_x[2]) << 8) |
(DP_GET_LSB(hdr->display_primaries_y[2]) << 16) |
(DP_GET_MSB(hdr->display_primaries_y[2]) << 24));
dp_write(MMSS_DP_GENERIC2_5 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->white_point_x) |
(DP_GET_MSB(hdr->white_point_x) << 8) |
(DP_GET_LSB(hdr->white_point_y) << 16) |
(DP_GET_MSB(hdr->white_point_y) << 24));
dp_write(MMSS_DP_GENERIC2_6 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->max_luminance) |
(DP_GET_MSB(hdr->max_luminance) << 8) |
(DP_GET_LSB(hdr->min_luminance) << 16) |
(DP_GET_MSB(hdr->min_luminance) << 24));
dp_write(MMSS_DP_GENERIC2_7 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (DP_GET_LSB(hdr->max_content_light_level) |
(DP_GET_MSB(hdr->max_content_light_level) << 8) |
(DP_GET_LSB(hdr->max_average_light_level) << 16) |
(DP_GET_MSB(hdr->max_average_light_level) << 24));
dp_write(MMSS_DP_GENERIC2_8 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
dp_write(MMSS_DP_GENERIC2_9 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
print_hex_dump_debug("[drm-dp] HDR: ",
DUMP_PREFIX_NONE, 16, 4, buf, off, false);
}
static void dp_catalog_panel_setup_vsc_sdp(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 header, parity, data, mst_offset = 0;
u8 off = 0;
u8 buf[SZ_128];
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
if (panel->stream_id == DP_STREAM_1)
mst_offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
/* HEADER BYTE 1 */
header = panel->vsc_colorimetry.header.HB1;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_1_BIT)
| (parity << PARITY_BYTE_1_BIT));
dp_write(MMSS_DP_GENERIC0_0 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
/* HEADER BYTE 2 */
header = panel->vsc_colorimetry.header.HB2;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_2_BIT)
| (parity << PARITY_BYTE_2_BIT));
dp_write(MMSS_DP_GENERIC0_1 + mst_offset, data);
/* HEADER BYTE 3 */
header = panel->vsc_colorimetry.header.HB3;
parity = dp_header_get_parity(header);
data = ((header << HEADER_BYTE_3_BIT)
| (parity << PARITY_BYTE_3_BIT));
data |= dp_read(MMSS_DP_GENERIC0_1 + mst_offset);
dp_write(MMSS_DP_GENERIC0_1 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
dp_write(MMSS_DP_GENERIC0_2 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
dp_write(MMSS_DP_GENERIC0_3 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
dp_write(MMSS_DP_GENERIC0_4 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
dp_write(MMSS_DP_GENERIC0_5 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = (panel->vsc_colorimetry.data[16] & 0xFF) |
((panel->vsc_colorimetry.data[17] & 0xFF) << 8) |
((panel->vsc_colorimetry.data[18] & 0x7) << 16);
dp_write(MMSS_DP_GENERIC0_6 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
data = 0;
dp_write(MMSS_DP_GENERIC0_7 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
dp_write(MMSS_DP_GENERIC0_8 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
dp_write(MMSS_DP_GENERIC0_9 + mst_offset, data);
memcpy(buf + off, &data, sizeof(data));
off += sizeof(data);
print_hex_dump_debug("[drm-dp] VSC: ",
DUMP_PREFIX_NONE, 16, 4, buf, off, false);
}
static void dp_catalog_panel_config_sdp(struct dp_catalog_panel *panel,
bool en)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 cfg, cfg2;
u32 sdp_cfg_off = 0;
u32 sdp_cfg2_off = 0;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1) {
sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
}
cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
if (en) {
/* GEN0_SDP_EN */
cfg |= BIT(17);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
/* GENERIC0_SDPSIZE */
cfg2 |= BIT(16);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
/* setup the GENERIC0 in case of en = true */
dp_catalog_panel_setup_vsc_sdp(panel);
} else {
/* GEN0_SDP_EN */
cfg &= ~BIT(17);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
/* GENERIC0_SDPSIZE */
cfg2 &= ~BIT(16);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
}
dp_catalog_panel_sdp_update(panel);
}
static void dp_catalog_panel_config_misc(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 reg_offset = 0;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1)
reg_offset = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
DP_DEBUG("misc settings = 0x%x\n", panel->misc_val);
dp_write(DP_MISC1_MISC0 + reg_offset, panel->misc_val);
}
static int dp_catalog_panel_set_colorspace(struct dp_catalog_panel *panel,
bool vsc_supported)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!panel) {
DP_ERR("invalid input\n");
return -EINVAL;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return -EINVAL;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (vsc_supported) {
dp_catalog_panel_setup_vsc_sdp(panel);
dp_catalog_panel_sdp_update(panel);
} else
dp_catalog_panel_config_misc(panel);
return 0;
}
static void dp_catalog_panel_config_hdr(struct dp_catalog_panel *panel, bool en,
u32 dhdr_max_pkts, bool flush)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 cfg, cfg2, cfg4, misc;
u32 sdp_cfg_off = 0;
u32 sdp_cfg2_off = 0;
u32 sdp_cfg4_off = 0;
u32 misc1_misc0_off = 0;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1) {
sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
sdp_cfg4_off = MMSS_DP1_SDP_CFG4 - MMSS_DP_SDP_CFG4;
misc1_misc0_off = DP1_MISC1_MISC0 - DP_MISC1_MISC0;
}
cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
misc = dp_read(DP_MISC1_MISC0 + misc1_misc0_off);
if (en) {
if (dhdr_max_pkts) {
/* VSCEXT_SDP_EN */
cfg |= BIT(16);
/* DHDR_EN, DHDR_PACKET_LIMIT */
cfg4 = (dhdr_max_pkts << 1) | BIT(0);
dp_write(MMSS_DP_SDP_CFG4 + sdp_cfg4_off, cfg4);
dp_catalog_panel_setup_vsif_infoframe_sdp(panel);
}
/* GEN2_SDP_EN */
cfg |= BIT(19);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
/* GENERIC2_SDPSIZE */
cfg2 |= BIT(20);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
dp_catalog_panel_setup_hdr_infoframe_sdp(panel);
if (panel->hdr_meta.eotf)
DP_DEBUG("Enabled\n");
else
DP_DEBUG("Reset\n");
} else {
/* VSCEXT_SDP_ENG */
cfg &= ~BIT(16) & ~BIT(19);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, cfg);
/* GENERIC0_SDPSIZE GENERIC2_SDPSIZE */
cfg2 &= ~BIT(20);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, cfg2);
/* DHDR_EN, DHDR_PACKET_LIMIT */
cfg4 = 0;
dp_write(MMSS_DP_SDP_CFG4 + sdp_cfg4_off, cfg4);
DP_DEBUG("Disabled\n");
}
if (flush) {
DP_DEBUG("flushing HDR metadata\n");
dp_catalog_panel_sdp_update(panel);
}
}
static void dp_catalog_panel_update_transfer_unit(
struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!panel || panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
dp_write(DP_VALID_BOUNDARY, panel->valid_boundary);
dp_write(DP_TU, panel->dp_tu);
dp_write(DP_VALID_BOUNDARY_2, panel->valid_boundary2);
}
static void dp_catalog_ctrl_state_ctrl(struct dp_catalog_ctrl *ctrl, u32 state)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
dp_write(DP_STATE_CTRL, state);
/* make sure to change the hw state */
wmb();
}
static void dp_catalog_ctrl_config_ctrl(struct dp_catalog_ctrl *ctrl, u8 ln_cnt)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 cfg;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
cfg = dp_read(DP_CONFIGURATION_CTRL);
/*
* Reset ASSR (alternate scrambler seed reset) by resetting BIT(10).
* ASSR should be set to disable for TPS4 link training pattern.
* Forcing it to 0 as the power on reset value of register enables it.
*/
cfg &= ~(BIT(4) | BIT(5) | BIT(10));
cfg |= (ln_cnt - 1) << 4;
dp_write(DP_CONFIGURATION_CTRL, cfg);
cfg = dp_read(DP_MAINLINK_CTRL);
cfg |= 0x02000000;
dp_write(DP_MAINLINK_CTRL, cfg);
DP_DEBUG("DP_MAINLINK_CTRL=0x%x\n", cfg);
}
static void dp_catalog_panel_config_ctrl(struct dp_catalog_panel *panel,
u32 cfg)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 strm_reg_off = 0, mainlink_ctrl;
u32 reg;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1)
strm_reg_off = DP1_CONFIGURATION_CTRL - DP_CONFIGURATION_CTRL;
DP_DEBUG("DP_CONFIGURATION_CTRL=0x%x\n", cfg);
dp_write(DP_CONFIGURATION_CTRL + strm_reg_off, cfg);
mainlink_ctrl = dp_read(DP_MAINLINK_CTRL);
if (panel->stream_id == DP_STREAM_0)
io_data = catalog->io.dp_p0;
else if (panel->stream_id == DP_STREAM_1)
io_data = catalog->io.dp_p1;
if (mainlink_ctrl & BIT(8))
dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x01);
else
dp_write(MMSS_DP_ASYNC_FIFO_CONFIG, 0x00);
reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
reg |= BIT(8);
dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
}
static void dp_catalog_panel_config_dto(struct dp_catalog_panel *panel,
bool ack)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 dsc_dto;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
switch (panel->stream_id) {
case DP_STREAM_0:
io_data = catalog->io.dp_p0;
break;
case DP_STREAM_1:
io_data = catalog->io.dp_p1;
break;
default:
DP_ERR("invalid stream id\n");
return;
}
dsc_dto = dp_read(MMSS_DP_DSC_DTO);
if (ack)
dsc_dto = BIT(1);
else
dsc_dto &= ~BIT(1);
dp_write(MMSS_DP_DSC_DTO, dsc_dto);
}
static void dp_catalog_ctrl_lane_mapping(struct dp_catalog_ctrl *ctrl,
bool flipped, char *lane_map)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
dp_write(DP_LOGICAL2PHYSICAL_LANE_MAPPING, 0xe4);
}
static void dp_catalog_ctrl_lane_pnswap(struct dp_catalog_ctrl *ctrl,
u8 ln_pnswap)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 cfg0, cfg1;
catalog = dp_catalog_get_priv(ctrl);
cfg0 = 0x0a;
cfg1 = 0x0a;
cfg0 |= ((ln_pnswap >> 0) & 0x1) << 0;
cfg0 |= ((ln_pnswap >> 1) & 0x1) << 2;
cfg1 |= ((ln_pnswap >> 2) & 0x1) << 0;
cfg1 |= ((ln_pnswap >> 3) & 0x1) << 2;
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_POL_INV, cfg0);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_POL_INV, cfg1);
}
static void dp_catalog_ctrl_mainlink_ctrl(struct dp_catalog_ctrl *ctrl,
bool enable)
{
u32 mainlink_ctrl, reg;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
if (enable) {
reg = dp_read(DP_MAINLINK_CTRL);
mainlink_ctrl = reg & ~(0x03);
dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
wmb(); /* make sure mainlink is turned off before reset */
mainlink_ctrl = reg | 0x02;
dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
wmb(); /* make sure mainlink entered reset */
mainlink_ctrl = reg & ~(0x03);
dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
wmb(); /* make sure mainlink reset done */
mainlink_ctrl = reg | 0x01;
dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
wmb(); /* make sure mainlink turned on */
} else {
mainlink_ctrl = dp_read(DP_MAINLINK_CTRL);
mainlink_ctrl &= ~BIT(0);
dp_write(DP_MAINLINK_CTRL, mainlink_ctrl);
}
}
static void dp_catalog_panel_config_msa(struct dp_catalog_panel *panel,
u32 rate, u32 stream_rate_khz)
{
u32 pixel_m, pixel_n;
u32 mvid, nvid;
u32 const nvid_fixed = 0x8000;
u32 const link_rate_hbr2 = 540000;
u32 const link_rate_hbr3 = 810000;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 strm_reg_off = 0;
u32 mvid_reg_off = 0, nvid_reg_off = 0;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_mmss_cc;
if (panel->stream_id == DP_STREAM_1)
strm_reg_off = MMSS_DP_PIXEL1_M - MMSS_DP_PIXEL_M;
pixel_m = dp_read(MMSS_DP_PIXEL_M + strm_reg_off);
pixel_n = dp_read(MMSS_DP_PIXEL_N + strm_reg_off);
DP_DEBUG("pixel_m=0x%x, pixel_n=0x%x\n", pixel_m, pixel_n);
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;
}
DP_DEBUG("rate = %d\n", rate);
if (panel->widebus_en)
mvid <<= 1;
if (link_rate_hbr2 == rate)
nvid *= 2;
if (link_rate_hbr3 == rate)
nvid *= 3;
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1) {
mvid_reg_off = DP1_SOFTWARE_MVID - DP_SOFTWARE_MVID;
nvid_reg_off = DP1_SOFTWARE_NVID - DP_SOFTWARE_NVID;
}
DP_DEBUG("mvid=0x%x, nvid=0x%x\n", mvid, nvid);
dp_write(DP_SOFTWARE_MVID + mvid_reg_off, mvid);
dp_write(DP_SOFTWARE_NVID + nvid_reg_off, nvid);
}
static void dp_catalog_ctrl_set_pattern(struct dp_catalog_ctrl *ctrl,
u32 pattern)
{
int bit, cnt = 10;
u32 data;
const u32 link_training_offset = 3;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
switch (pattern) {
case DP_TRAINING_PATTERN_4:
bit = 3;
break;
case DP_TRAINING_PATTERN_3:
case DP_TRAINING_PATTERN_2:
case DP_TRAINING_PATTERN_1:
bit = pattern - 1;
break;
default:
DP_ERR("invalid pattern\n");
return;
}
DP_DEBUG("hw: bit=%d train=%d\n", bit, pattern);
dp_write(DP_STATE_CTRL, BIT(bit));
bit += link_training_offset;
while (cnt--) {
data = dp_read(DP_MAINLINK_READY);
if (data & BIT(bit))
break;
}
if (cnt == 0)
DP_ERR("set link_train=%d failed\n", pattern);
}
static void dp_catalog_ctrl_usb_reset(struct dp_catalog_ctrl *ctrl, bool flip)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.usb3_dp_com;
DP_DEBUG("Program PHYMODE to DP only\n");
dp_write(USB3_DP_COM_RESET_OVRD_CTRL, 0x0a);
dp_write(USB3_DP_COM_PHY_MODE_CTRL, 0x02);
dp_write(USB3_DP_COM_SW_RESET, 0x01);
/* make sure usb3 com phy software reset is done */
wmb();
if (!flip) /* CC1 */
dp_write(USB3_DP_COM_TYPEC_CTRL, 0x02);
else /* CC2 */
dp_write(USB3_DP_COM_TYPEC_CTRL, 0x03);
dp_write(USB3_DP_COM_SWI_CTRL, 0x00);
dp_write(USB3_DP_COM_SW_RESET, 0x00);
/* make sure the software reset is done */
wmb();
dp_write(USB3_DP_COM_POWER_DOWN_CTRL, 0x01);
dp_write(USB3_DP_COM_RESET_OVRD_CTRL, 0x00);
/* make sure phy is brought out of reset */
wmb();
}
static int dp_catalog_ctrl_setup_misr(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 val;
if (!ctrl) {
DP_ERR("invalid input\n");
return -EINVAL;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_phy;
dp_write(DP_PHY_MISR_CTRL, 0x3);
/* make sure misr hw is reset */
wmb();
dp_write(DP_PHY_MISR_CTRL, 0x1);
/* make sure misr is brought out of reset */
wmb();
io_data = catalog->io.dp_link;
val = 1; // frame count
val |= BIT(10); // clear status
val |= BIT(8); // enable
dp_write(DP_MISR40_CTRL, val);
/* make sure misr control is applied */
wmb();
return 0;
}
static int dp_catalog_ctrl_read_misr(struct dp_catalog_ctrl *ctrl, struct dp_misr40_data *data)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 val;
int i, j;
u32 addr;
if (!ctrl) {
DP_ERR("invalid input\n");
return -EINVAL;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_phy;
val = dp_read(DP_PHY_MISR_STATUS);
if (!val) {
DP_WARN("phy misr not ready!");
return -EAGAIN;
}
addr = DP_PHY_MISR_TX0;
for (i = 0; i < 8; i++) {
data->phy_misr[i] = 0;
for (j = 0; j < 4; j++) {
val = dp_read(addr) & 0xff;
data->phy_misr[i] |= val << (j * 8);
addr += 4;
}
}
io_data = catalog->io.dp_link;
for (i = 0; i < 8; i++)
data->ctrl_misr[i] = dp_read(DP_MISR40_TX0 + (i * 4));
return 0;
}
static void dp_catalog_panel_tpg_cfg(struct dp_catalog_panel *panel, u32 pattern)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 reg;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
if (panel->stream_id == DP_STREAM_0)
io_data = catalog->io.dp_p0;
else if (panel->stream_id == DP_STREAM_1)
io_data = catalog->io.dp_p1;
if (!pattern) {
dp_write(MMSS_DP_TPG_MAIN_CONTROL, 0x0);
dp_write(MMSS_DP_BIST_ENABLE, 0x0);
reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
reg &= ~0x1;
dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
wmb(); /* ensure Timing generator is turned off */
return;
}
if (pattern > DP_TPG_PATTERN_MAX)
pattern = DP_TPG_PATTERN_DEFAULT;
dp_write(MMSS_DP_INTF_HSYNC_CTL,
panel->hsync_ctl);
dp_write(MMSS_DP_INTF_VSYNC_PERIOD_F0,
panel->vsync_period * panel->hsync_period);
dp_write(MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F0,
panel->v_sync_width * panel->hsync_period);
dp_write(MMSS_DP_INTF_VSYNC_PERIOD_F1, 0);
dp_write(MMSS_DP_INTF_VSYNC_PULSE_WIDTH_F1, 0);
dp_write(MMSS_DP_INTF_DISPLAY_HCTL, panel->display_hctl);
dp_write(MMSS_DP_INTF_ACTIVE_HCTL, 0);
dp_write(MMSS_INTF_DISPLAY_V_START_F0, panel->display_v_start);
dp_write(MMSS_DP_INTF_DISPLAY_V_END_F0, panel->display_v_end);
dp_write(MMSS_INTF_DISPLAY_V_START_F1, 0);
dp_write(MMSS_DP_INTF_DISPLAY_V_END_F1, 0);
dp_write(MMSS_DP_INTF_ACTIVE_V_START_F0, 0);
dp_write(MMSS_DP_INTF_ACTIVE_V_END_F0, 0);
dp_write(MMSS_DP_INTF_ACTIVE_V_START_F1, 0);
dp_write(MMSS_DP_INTF_ACTIVE_V_END_F1, 0);
dp_write(MMSS_DP_INTF_POLARITY_CTL, 0);
wmb(); /* ensure TPG registers are programmed */
dp_write(MMSS_DP_TPG_MAIN_CONTROL, (1 << pattern));
dp_write(MMSS_DP_TPG_VIDEO_CONFIG, 0x5);
wmb(); /* ensure TPG config is programmed */
dp_write(MMSS_DP_BIST_ENABLE, 0x1);
reg = dp_read(MMSS_DP_TIMING_ENGINE_EN);
reg |= 0x1;
dp_write(MMSS_DP_TIMING_ENGINE_EN, reg);
wmb(); /* ensure Timing generator is turned on */
}
static void dp_catalog_panel_dsc_cfg(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 reg, offset;
int i;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
if (panel->stream_id == DP_STREAM_0)
io_data = catalog->io.dp_p0;
else
io_data = catalog->io.dp_p1;
dp_write(MMSS_DP_DSC_DTO_COUNT, panel->dsc.dto_count);
reg = dp_read(MMSS_DP_DSC_DTO);
if (panel->dsc.dto_en) {
reg |= BIT(0);
reg |= BIT(3);
reg |= (panel->dsc.dto_n << 8);
reg |= (panel->dsc.dto_d << 16);
}
dp_write(MMSS_DP_DSC_DTO, reg);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_0)
offset = 0;
else
offset = DP1_COMPRESSION_MODE_CTRL - DP_COMPRESSION_MODE_CTRL;
dp_write(DP_PPS_HB_0_3 + offset, 0x7F1000);
dp_write(DP_PPS_PB_0_3 + offset, 0xA22300);
for (i = 0; i < panel->dsc.parity_word_len; i++)
dp_write(DP_PPS_PB_4_7 + (i << 2) + offset,
panel->dsc.parity_word[i]);
for (i = 0; i < panel->dsc.pps_word_len; i++)
dp_write(DP_PPS_PPS_0_3 + (i << 2) + offset,
panel->dsc.pps_word[i]);
reg = 0;
if (panel->dsc.dsc_en) {
reg = BIT(0);
reg |= (panel->dsc.eol_byte_num << 3);
reg |= (panel->dsc.slice_per_pkt << 5);
reg |= (panel->dsc.bytes_per_pkt << 16);
reg |= (panel->dsc.be_in_lane << 10);
}
dp_write(DP_COMPRESSION_MODE_CTRL + offset, reg);
DP_DEBUG("compression:0x%x for stream:%d\n",
reg, panel->stream_id);
}
static void dp_catalog_panel_dp_flush(struct dp_catalog_panel *panel,
enum dp_flush_bit flush_bit)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 dp_flush, offset;
struct dp_dsc_cfg_data *dsc;
if (!panel) {
DP_ERR("invalid input\n");
return;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
dsc = &panel->dsc;
if (panel->stream_id == DP_STREAM_0)
offset = 0;
else
offset = MMSS_DP1_FLUSH - MMSS_DP_FLUSH;
dp_flush = dp_read(MMSS_DP_FLUSH + offset);
if ((flush_bit == DP_PPS_FLUSH) &&
dsc->continuous_pps)
dp_flush &= ~BIT(2);
dp_flush |= BIT(flush_bit);
dp_write(MMSS_DP_FLUSH + offset, dp_flush);
}
static void dp_catalog_panel_pps_flush(struct dp_catalog_panel *panel)
{
dp_catalog_panel_dp_flush(panel, DP_PPS_FLUSH);
DP_DEBUG("pps flush for stream:%d\n", panel->stream_id);
}
static void dp_catalog_panel_dhdr_flush(struct dp_catalog_panel *panel)
{
dp_catalog_panel_dp_flush(panel, DP_DHDR_FLUSH);
DP_DEBUG("dhdr flush for stream:%d\n", panel->stream_id);
}
static bool dp_catalog_panel_dhdr_busy(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 dp_flush, offset;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return false;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_0)
offset = 0;
else
offset = MMSS_DP1_FLUSH - MMSS_DP_FLUSH;
dp_flush = dp_read(MMSS_DP_FLUSH + offset);
return dp_flush & BIT(DP_DHDR_FLUSH) ? true : false;
}
static int dp_catalog_panel_get_src_crc(struct dp_catalog_panel *panel, u16 *crc)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 offset;
u32 reg;
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
return -EINVAL;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_0)
offset = MMSS_DP_PSR_CRC_RG;
else
offset = MMSS_DP1_CRC_RG;
reg = dp_read(offset); //GR
crc[0] = reg & 0xffff;
crc[1] = reg >> 16;
crc[2] = dp_read(offset + 4); //B
return 0;
}
static void dp_catalog_ctrl_reset(struct dp_catalog_ctrl *ctrl)
{
u32 sw_reset;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
sw_reset = dp_read(DP_SW_RESET);
sw_reset |= BIT(0);
dp_write(DP_SW_RESET, sw_reset);
usleep_range(1000, 1010); /* h/w recommended delay */
sw_reset &= ~BIT(0);
dp_write(DP_SW_RESET, sw_reset);
}
static bool dp_catalog_ctrl_mainlink_ready(struct dp_catalog_ctrl *ctrl)
{
u32 data;
int cnt = 10;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
goto end;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
while (--cnt) {
/* DP_MAINLINK_READY */
data = dp_read(DP_MAINLINK_READY);
if (data & BIT(0))
return true;
usleep_range(1000, 1010); /* 1ms wait before next reg read */
}
DP_ERR("mainlink not ready\n");
end:
return false;
}
static void dp_catalog_ctrl_enable_irq(struct dp_catalog_ctrl *ctrl,
bool enable)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
if (enable) {
dp_write(DP_INTR_STATUS, DP_INTR_MASK1);
dp_write(DP_INTR_STATUS2, DP_INTR_MASK2);
dp_write(DP_INTR_STATUS5, DP_INTR_MASK5);
} else {
/* disable interrupts */
dp_write(DP_INTR_STATUS, 0x00);
dp_write(DP_INTR_STATUS2, 0x00);
dp_write(DP_INTR_STATUS5, 0x00);
wmb();
/* clear all pending interrupts */
dp_write(DP_INTR_STATUS, DP_INTERRUPT_STATUS1 << 1);
dp_write(DP_INTR_STATUS2, DP_INTERRUPT_STATUS2 << 1);
dp_write(DP_INTR_STATUS5, DP_INTERRUPT_STATUS5 << 1);
wmb();
}
}
static void dp_catalog_ctrl_get_interrupt(struct dp_catalog_ctrl *ctrl)
{
u32 ack = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
ctrl->isr = dp_read(DP_INTR_STATUS2);
ctrl->isr &= ~DP_INTR_MASK2;
ack = ctrl->isr & DP_INTERRUPT_STATUS2;
ack <<= 1;
ack |= DP_INTR_MASK2;
dp_write(DP_INTR_STATUS2, ack);
ctrl->isr5 = dp_read(DP_INTR_STATUS5);
ctrl->isr5 &= ~DP_INTR_MASK5;
ack = ctrl->isr5 & DP_INTERRUPT_STATUS5;
ack <<= 1;
ack |= DP_INTR_MASK5;
dp_write(DP_INTR_STATUS5, ack);
}
static void dp_catalog_ctrl_phy_reset(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_ahb;
dp_write(DP_PHY_CTRL, 0x5); /* bit 0 & 2 */
usleep_range(1000, 1010); /* h/w recommended delay */
dp_write(DP_PHY_CTRL, 0x0);
wmb(); /* make sure PHY reset done */
}
static void dp_catalog_ctrl_phy_lane_cfg(struct dp_catalog_ctrl *ctrl,
bool flipped, u8 ln_cnt)
{
u32 info = 0x0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u8 orientation = BIT(!!flipped);
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_phy;
info |= (ln_cnt & 0x0F);
info |= ((orientation & 0x0F) << 4);
DP_DEBUG("Shared Info = 0x%x\n", info);
dp_write(DP_PHY_SPARE0, info);
}
static void dp_catalog_ctrl_update_vx_px(struct dp_catalog_ctrl *ctrl,
u8 v_level, u8 p_level, bool high)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u8 value0, value1;
u32 version;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
DP_DEBUG("hw: v=%d p=%d\n", v_level, p_level);
io_data = catalog->io.dp_ahb;
version = dp_read(DP_HW_VERSION);
if (version == 0x10020004) {
if (high) {
value0 = vm_voltage_swing_hbr3_hbr2[v_level][p_level];
value1 = vm_pre_emphasis_hbr3_hbr2[v_level][p_level];
} else {
value0 = vm_voltage_swing_hbr_rbr[v_level][p_level];
value1 = vm_pre_emphasis_hbr_rbr[v_level][p_level];
}
} else {
value0 = vm_voltage_swing[v_level][p_level];
value1 = vm_pre_emphasis[v_level][p_level];
}
/* program default setting first */
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL, 0x2A);
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_DRV_LVL, 0x2A);
dp_write(TXn_TX_EMP_POST1_LVL, 0x20);
/* Enable MUX to use Cursor values from these registers */
value0 |= BIT(5);
value1 |= BIT(5);
/* Configure host and panel only if both values are allowed */
if (value0 != 0xFF && value1 != 0xFF) {
io_data = catalog->io.dp_ln_tx0;
dp_write(TXn_TX_DRV_LVL, value0);
dp_write(TXn_TX_EMP_POST1_LVL, value1);
io_data = catalog->io.dp_ln_tx1;
dp_write(TXn_TX_DRV_LVL, value0);
dp_write(TXn_TX_EMP_POST1_LVL, value1);
DP_DEBUG("hw: vx_value=0x%x px_value=0x%x\n",
value0, value1);
} else {
DP_ERR("invalid vx (0x%x=0x%x), px (0x%x=0x%x\n",
v_level, value0, p_level, value1);
}
}
static void dp_catalog_ctrl_send_phy_pattern(struct dp_catalog_ctrl *ctrl,
u32 pattern)
{
struct dp_catalog_private *catalog;
u32 value = 0x0;
struct dp_io_data *io_data = NULL;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
dp_write(DP_STATE_CTRL, 0x0);
switch (pattern) {
case DP_PHY_TEST_PATTERN_D10_2:
dp_write(DP_STATE_CTRL, 0x1);
break;
case DP_PHY_TEST_PATTERN_ERROR_COUNT:
value &= ~(1 << 16);
dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
value |= 0xFC;
dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
dp_write(DP_MAINLINK_LEVELS, 0x2);
dp_write(DP_STATE_CTRL, 0x10);
break;
case DP_PHY_TEST_PATTERN_PRBS7:
dp_write(DP_STATE_CTRL, 0x20);
break;
case DP_PHY_TEST_PATTERN_80BIT_CUSTOM:
dp_write(DP_STATE_CTRL, 0x40);
/* 00111110000011111000001111100000 */
dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG0, 0x3E0F83E0);
/* 00001111100000111110000011111000 */
dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG1, 0x0F83E0F8);
/* 1111100000111110 */
dp_write(DP_TEST_80BIT_CUSTOM_PATTERN_REG2, 0x0000F83E);
break;
case DP_PHY_TEST_PATTERN_CP2520:
value = dp_read(DP_MAINLINK_CTRL);
value &= ~BIT(4);
dp_write(DP_MAINLINK_CTRL, value);
value = BIT(16);
dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
value |= 0xFC;
dp_write(DP_HBR2_COMPLIANCE_SCRAMBLER_RESET, value);
dp_write(DP_MAINLINK_LEVELS, 0x2);
dp_write(DP_STATE_CTRL, 0x10);
value = dp_read(DP_MAINLINK_CTRL);
value |= BIT(0);
dp_write(DP_MAINLINK_CTRL, value);
break;
case DP_PHY_TEST_PATTERN_CP2520_3:
dp_write(DP_MAINLINK_CTRL, 0x01);
dp_write(DP_STATE_CTRL, 0x8);
break;
default:
DP_DEBUG("No valid test pattern requested: 0x%x\n", pattern);
return;
}
/* Make sure the test pattern is programmed in the hardware */
wmb();
}
static u32 dp_catalog_ctrl_read_phy_pattern(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
if (!ctrl) {
DP_ERR("invalid input\n");
return 0;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
return dp_read(DP_MAINLINK_READY);
}
static void dp_catalog_ctrl_fec_config(struct dp_catalog_ctrl *ctrl,
bool enable)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 reg;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
reg = dp_read(DP_MAINLINK_CTRL);
/*
* fec_en = BIT(12)
* fec_seq_mode = BIT(22)
* sde_flush = BIT(23) | BIT(24)
* fb_boundary_sel = BIT(25)
*/
if (enable)
reg |= BIT(12) | BIT(22) | BIT(23) | BIT(24) | BIT(25);
else
reg &= ~BIT(12);
dp_write(DP_MAINLINK_CTRL, reg);
/* make sure mainlink configuration is updated with fec sequence */
wmb();
}
u32 dp_catalog_get_dp_core_version(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!dp_catalog) {
DP_ERR("invalid input\n");
return 0;
}
catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
if (catalog->dp_core_version)
return catalog->dp_core_version;
io_data = catalog->io.dp_ahb;
return dp_read(DP_HW_VERSION);
}
u32 dp_catalog_get_dp_phy_version(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!dp_catalog) {
DP_ERR("invalid input\n");
return 0;
}
catalog = container_of(dp_catalog, struct dp_catalog_private, dp_catalog);
if (catalog->dp_phy_version)
return catalog->dp_phy_version;
io_data = catalog->io.dp_phy;
catalog->dp_phy_version = (dp_read(DP_PHY_REVISION_ID3) << 24) |
(dp_read(DP_PHY_REVISION_ID2) << 16) |
(dp_read(DP_PHY_REVISION_ID1) << 8) |
dp_read(DP_PHY_REVISION_ID0);
return catalog->dp_phy_version;
}
static int dp_catalog_reg_dump(struct dp_catalog *dp_catalog,
char *name, u8 **out_buf, u32 *out_buf_len)
{
int ret = 0;
u8 *buf;
u32 len;
struct dp_io_data *io_data;
struct dp_catalog_private *catalog;
struct dp_parser *parser;
if (!dp_catalog) {
DP_ERR("invalid input\n");
return -EINVAL;
}
catalog = container_of(dp_catalog, struct dp_catalog_private,
dp_catalog);
parser = catalog->parser;
parser->get_io_buf(parser, name);
io_data = parser->get_io(parser, name);
if (!io_data) {
DP_ERR("IO %s not found\n", name);
ret = -EINVAL;
goto end;
}
buf = io_data->buf;
len = io_data->io.len;
if (!buf || !len) {
DP_ERR("no buffer available\n");
ret = -ENOMEM;
goto end;
}
if (!strcmp(catalog->exe_mode, "hw") ||
!strcmp(catalog->exe_mode, "all")) {
u32 i, data;
u32 const rowsize = 4;
void __iomem *addr = io_data->io.base;
memset(buf, 0, len);
for (i = 0; i < len / rowsize; i++) {
data = readl_relaxed(addr);
memcpy(buf + (rowsize * i), &data, sizeof(u32));
addr += rowsize;
}
}
*out_buf = buf;
*out_buf_len = len;
end:
if (ret)
parser->clear_io_buf(parser);
return ret;
}
static void dp_catalog_ctrl_mst_config(struct dp_catalog_ctrl *ctrl,
bool enable)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 reg;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
reg = dp_read(DP_MAINLINK_CTRL);
if (enable)
reg |= (0x04000100);
else
reg &= ~(0x04000100);
dp_write(DP_MAINLINK_CTRL, reg);
/* make sure mainlink MST configuration is updated */
wmb();
}
static void dp_catalog_ctrl_trigger_act(struct dp_catalog_ctrl *ctrl)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
if (!ctrl) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
dp_write(DP_MST_ACT, 0x1);
/* make sure ACT signal is performed */
wmb();
}
static void dp_catalog_ctrl_read_act_complete_sts(struct dp_catalog_ctrl *ctrl,
bool *sts)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 reg;
if (!ctrl || !sts) {
DP_ERR("invalid input\n");
return;
}
*sts = false;
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
reg = dp_read(DP_MST_ACT);
if (!reg)
*sts = true;
}
static void dp_catalog_ctrl_channel_alloc(struct dp_catalog_ctrl *ctrl,
u32 ch, u32 ch_start_slot, u32 tot_slot_cnt)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 i, slot_reg_1, slot_reg_2, slot;
u32 reg_off = 0;
int const num_slots_per_reg = 32;
if (!ctrl || ch >= DP_STREAM_MAX) {
DP_ERR("invalid input. ch %d\n", ch);
return;
}
if (ch_start_slot > DP_MAX_TIME_SLOTS ||
(ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
DP_ERR("invalid slots start %d, tot %d\n",
ch_start_slot, tot_slot_cnt);
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
DP_DEBUG("ch %d, start_slot %d, tot_slot %d\n",
ch, ch_start_slot, tot_slot_cnt);
if (ch == DP_STREAM_1)
reg_off = DP_DP1_TIMESLOT_1_32 - DP_DP0_TIMESLOT_1_32;
slot_reg_1 = 0;
slot_reg_2 = 0;
if (ch_start_slot && tot_slot_cnt) {
ch_start_slot--;
for (i = 0; i < tot_slot_cnt; i++) {
if (ch_start_slot < num_slots_per_reg) {
slot_reg_1 |= BIT(ch_start_slot);
} else {
slot = ch_start_slot - num_slots_per_reg;
slot_reg_2 |= BIT(slot);
}
ch_start_slot++;
}
}
DP_DEBUG("ch:%d slot_reg_1:%d, slot_reg_2:%d\n", ch,
slot_reg_1, slot_reg_2);
dp_write(DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
dp_write(DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
}
static void dp_catalog_ctrl_channel_dealloc(struct dp_catalog_ctrl *ctrl,
u32 ch, u32 ch_start_slot, u32 tot_slot_cnt)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 i, slot_reg_1, slot_reg_2, slot;
u32 reg_off = 0;
if (!ctrl || ch >= DP_STREAM_MAX) {
DP_ERR("invalid input. ch %d\n", ch);
return;
}
if (ch_start_slot > DP_MAX_TIME_SLOTS ||
(ch_start_slot + tot_slot_cnt > DP_MAX_TIME_SLOTS)) {
DP_ERR("invalid slots start %d, tot %d\n",
ch_start_slot, tot_slot_cnt);
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
DP_DEBUG("dealloc ch %d, start_slot %d, tot_slot %d\n",
ch, ch_start_slot, tot_slot_cnt);
if (ch == DP_STREAM_1)
reg_off = DP_DP1_TIMESLOT_1_32 - DP_DP0_TIMESLOT_1_32;
slot_reg_1 = dp_read(DP_DP0_TIMESLOT_1_32 + reg_off);
slot_reg_2 = dp_read(DP_DP0_TIMESLOT_33_63 + reg_off);
ch_start_slot = ch_start_slot - 1;
for (i = 0; i < tot_slot_cnt; i++) {
if (ch_start_slot < 33) {
slot_reg_1 &= ~BIT(ch_start_slot);
} else {
slot = ch_start_slot - 33;
slot_reg_2 &= ~BIT(slot);
}
ch_start_slot++;
}
DP_DEBUG("dealloc ch:%d slot_reg_1:%d, slot_reg_2:%d\n", ch,
slot_reg_1, slot_reg_2);
dp_write(DP_DP0_TIMESLOT_1_32 + reg_off, slot_reg_1);
dp_write(DP_DP0_TIMESLOT_33_63 + reg_off, slot_reg_2);
}
static void dp_catalog_ctrl_update_rg(struct dp_catalog_ctrl *ctrl, u32 ch,
u32 x_int, u32 y_frac_enum)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data = NULL;
u32 rg, reg_off = 0;
if (!ctrl || ch >= DP_STREAM_MAX) {
DP_ERR("invalid input. ch %d\n", ch);
return;
}
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
rg = y_frac_enum;
rg |= (x_int << 16);
DP_DEBUG("ch: %d x_int:%d y_frac_enum:%d rg:%d\n", ch, x_int,
y_frac_enum, rg);
if (ch == DP_STREAM_1)
reg_off = DP_DP1_RG - DP_DP0_RG;
dp_write(DP_DP0_RG + reg_off, rg);
}
static void dp_catalog_ctrl_mainlink_levels(struct dp_catalog_ctrl *ctrl,
u8 lane_cnt)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 mainlink_levels, safe_to_exit_level = 14;
catalog = dp_catalog_get_priv(ctrl);
io_data = catalog->io.dp_link;
switch (lane_cnt) {
case 1:
safe_to_exit_level = 14;
break;
case 2:
safe_to_exit_level = 8;
break;
case 4:
safe_to_exit_level = 5;
break;
default:
DP_DEBUG("setting the default safe_to_exit_level = %u\n",
safe_to_exit_level);
break;
}
mainlink_levels = dp_read(DP_MAINLINK_LEVELS);
mainlink_levels &= 0xFE0;
mainlink_levels |= safe_to_exit_level;
DP_DEBUG("mainlink_level = 0x%x, safe_to_exit_level = 0x%x\n",
mainlink_levels, safe_to_exit_level);
dp_write(DP_MAINLINK_LEVELS, mainlink_levels);
}
/* panel related catalog functions */
static int dp_catalog_panel_timing_cfg(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 offset = 0, reg;
if (!panel) {
DP_ERR("invalid input\n");
goto end;
}
if (panel->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream_id:%d\n", panel->stream_id);
goto end;
}
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1)
offset = DP1_TOTAL_HOR_VER - DP_TOTAL_HOR_VER;
dp_write(DP_TOTAL_HOR_VER + offset, panel->total);
dp_write(DP_START_HOR_VER_FROM_SYNC + offset, panel->sync_start);
dp_write(DP_HSYNC_VSYNC_WIDTH_POLARITY + offset, panel->width_blanking);
dp_write(DP_ACTIVE_HOR_VER + offset, panel->dp_active);
if (panel->stream_id == DP_STREAM_0)
io_data = catalog->io.dp_p0;
else
io_data = catalog->io.dp_p1;
reg = dp_read(MMSS_DP_INTF_CONFIG);
if (panel->widebus_en)
reg |= BIT(4);
else
reg &= ~BIT(4);
dp_write(MMSS_DP_INTF_CONFIG, reg);
end:
return 0;
}
static void dp_catalog_hpd_config_hpd(struct dp_catalog_hpd *hpd, bool en)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!hpd) {
DP_ERR("invalid input\n");
return;
}
catalog = dp_catalog_get_priv(hpd);
io_data = catalog->io.dp_aux;
if (en) {
u32 reftimer = dp_read(DP_DP_HPD_REFTIMER);
/* Arm only the UNPLUG and HPD_IRQ interrupts */
dp_write(DP_DP_HPD_INT_ACK, 0xF);
dp_write(DP_DP_HPD_INT_MASK, 0xA);
/* Enable REFTIMER to count 1ms */
reftimer |= BIT(16);
dp_write(DP_DP_HPD_REFTIMER, reftimer);
/* Connect_time is 250us & disconnect_time is 2ms */
dp_write(DP_DP_HPD_EVENT_TIME_0, 0x3E800FA);
dp_write(DP_DP_HPD_EVENT_TIME_1, 0x1F407D0);
/* Enable HPD */
dp_write(DP_DP_HPD_CTRL, 0x1);
} else {
/* Disable HPD */
dp_write(DP_DP_HPD_CTRL, 0x0);
}
}
static u32 dp_catalog_hpd_get_interrupt(struct dp_catalog_hpd *hpd)
{
u32 isr = 0;
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
if (!hpd) {
DP_ERR("invalid input\n");
return isr;
}
catalog = dp_catalog_get_priv(hpd);
io_data = catalog->io.dp_aux;
isr = dp_read(DP_DP_HPD_INT_STATUS);
dp_write(DP_DP_HPD_INT_ACK, (isr & 0xf));
return isr;
}
static void dp_catalog_audio_init(struct dp_catalog_audio *audio)
{
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 (!audio)
return;
catalog = dp_catalog_get_priv(audio);
catalog->audio_map = sdp_map;
}
static void dp_catalog_audio_config_sdp(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 sdp_cfg = 0, sdp_cfg_off = 0;
u32 sdp_cfg2 = 0, sdp_cfg2_off = 0;
if (!audio)
return;
if (audio->stream_id >= DP_STREAM_MAX) {
DP_ERR("invalid stream id:%d\n", audio->stream_id);
return;
}
if (audio->stream_id == DP_STREAM_1) {
sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
}
catalog = dp_catalog_get_priv(audio);
io_data = catalog->io.dp_link;
sdp_cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
/* 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);
DP_DEBUG("sdp_cfg = 0x%x\n", sdp_cfg);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, sdp_cfg);
sdp_cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg_off);
/* IFRM_REGSRC -> Do not use reg values */
sdp_cfg2 &= ~BIT(0);
/* AUDIO_STREAM_HB3_REGSRC-> Do not use reg values */
sdp_cfg2 &= ~BIT(1);
DP_DEBUG("sdp_cfg2 = 0x%x\n", sdp_cfg2);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg_off, sdp_cfg2);
}
static void dp_catalog_audio_get_header(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_io_data *io_data;
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
if (!audio)
return;
catalog = dp_catalog_get_priv(audio);
io_data = catalog->io.dp_link;
sdp_map = catalog->audio_map;
sdp = audio->sdp_type;
header = audio->sdp_header;
audio->data = dp_read(sdp_map[sdp][header]);
}
static void dp_catalog_audio_set_header(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
u32 (*sdp_map)[DP_AUDIO_SDP_HEADER_MAX];
struct dp_io_data *io_data;
enum dp_catalog_audio_sdp_type sdp;
enum dp_catalog_audio_header_type header;
u32 data;
if (!audio)
return;
catalog = dp_catalog_get_priv(audio);
io_data = catalog->io.dp_link;
sdp_map = catalog->audio_map;
sdp = audio->sdp_type;
header = audio->sdp_header;
data = audio->data;
dp_write(sdp_map[sdp][header], data);
}
static void dp_catalog_audio_config_acr(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 acr_ctrl, select;
catalog = dp_catalog_get_priv(audio);
select = audio->data;
io_data = catalog->io.dp_link;
acr_ctrl = select << 4 | BIT(31) | BIT(8) | BIT(14);
DP_DEBUG("select = 0x%x, acr_ctrl = 0x%x\n", select, acr_ctrl);
dp_write(MMSS_DP_AUDIO_ACR_CTRL, acr_ctrl);
}
static void dp_catalog_audio_enable(struct dp_catalog_audio *audio)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
bool enable;
u32 audio_ctrl;
catalog = dp_catalog_get_priv(audio);
io_data = catalog->io.dp_link;
enable = !!audio->data;
audio_ctrl = dp_read(MMSS_DP_AUDIO_CFG);
if (enable)
audio_ctrl |= BIT(0);
else
audio_ctrl &= ~BIT(0);
DP_DEBUG("dp_audio_cfg = 0x%x\n", audio_ctrl);
dp_write(MMSS_DP_AUDIO_CFG, audio_ctrl);
/* make sure audio engine is disabled */
wmb();
}
static void dp_catalog_config_spd_header(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 value, new_value, offset = 0;
u8 parity_byte;
if (!panel || panel->stream_id >= DP_STREAM_MAX)
return;
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1)
offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
/* Config header and parity byte 1 */
value = dp_read(MMSS_DP_GENERIC1_0 + offset);
new_value = 0x83;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_1_BIT)
| (parity_byte << PARITY_BYTE_1_BIT));
DP_DEBUG("Header Byte 1: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_write(MMSS_DP_GENERIC1_0 + offset, value);
/* Config header and parity byte 2 */
value = dp_read(MMSS_DP_GENERIC1_1 + offset);
new_value = 0x1b;
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_2_BIT)
| (parity_byte << PARITY_BYTE_2_BIT));
DP_DEBUG("Header Byte 2: value = 0x%x, parity_byte = 0x%x\n",
value, parity_byte);
dp_write(MMSS_DP_GENERIC1_1 + offset, value);
/* Config header and parity byte 3 */
value = dp_read(MMSS_DP_GENERIC1_1 + offset);
new_value = (0x0 | (0x12 << 2));
parity_byte = dp_header_get_parity(new_value);
value |= ((new_value << HEADER_BYTE_3_BIT)
| (parity_byte << PARITY_BYTE_3_BIT));
DP_DEBUG("Header Byte 3: value = 0x%x, parity_byte = 0x%x\n",
new_value, parity_byte);
dp_write(MMSS_DP_GENERIC1_1 + offset, value);
}
static void dp_catalog_panel_config_spd(struct dp_catalog_panel *panel)
{
struct dp_catalog_private *catalog;
struct dp_io_data *io_data;
u32 spd_cfg = 0, spd_cfg2 = 0;
u8 *vendor = NULL, *product = NULL;
u32 offset = 0;
u32 sdp_cfg_off = 0;
u32 sdp_cfg2_off = 0;
/*
* Source Device Information
* 00h unknown
* 01h Digital STB
* 02h DVD
* 03h D-VHS
* 04h HDD Video
* 05h DVC
* 06h DSC
* 07h Video CD
* 08h Game
* 09h PC general
* 0ah Bluray-Disc
* 0bh Super Audio CD
* 0ch HD DVD
* 0dh PMP
* 0eh-ffh reserved
*/
u32 device_type = 0;
if (!panel || panel->stream_id >= DP_STREAM_MAX)
return;
catalog = dp_catalog_get_priv(panel);
io_data = catalog->io.dp_link;
if (panel->stream_id == DP_STREAM_1)
offset = MMSS_DP1_GENERIC0_0 - MMSS_DP_GENERIC0_0;
dp_catalog_config_spd_header(panel);
vendor = panel->spd_vendor_name;
product = panel->spd_product_description;
dp_write(MMSS_DP_GENERIC1_2 + offset,
((vendor[0] & 0x7f) |
((vendor[1] & 0x7f) << 8) |
((vendor[2] & 0x7f) << 16) |
((vendor[3] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_3 + offset,
((vendor[4] & 0x7f) |
((vendor[5] & 0x7f) << 8) |
((vendor[6] & 0x7f) << 16) |
((vendor[7] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_4 + offset,
((product[0] & 0x7f) |
((product[1] & 0x7f) << 8) |
((product[2] & 0x7f) << 16) |
((product[3] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_5 + offset,
((product[4] & 0x7f) |
((product[5] & 0x7f) << 8) |
((product[6] & 0x7f) << 16) |
((product[7] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_6 + offset,
((product[8] & 0x7f) |
((product[9] & 0x7f) << 8) |
((product[10] & 0x7f) << 16) |
((product[11] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_7 + offset,
((product[12] & 0x7f) |
((product[13] & 0x7f) << 8) |
((product[14] & 0x7f) << 16) |
((product[15] & 0x7f) << 24)));
dp_write(MMSS_DP_GENERIC1_8 + offset, device_type);
dp_write(MMSS_DP_GENERIC1_9 + offset, 0x00);
if (panel->stream_id == DP_STREAM_1) {
sdp_cfg_off = MMSS_DP1_SDP_CFG - MMSS_DP_SDP_CFG;
sdp_cfg2_off = MMSS_DP1_SDP_CFG2 - MMSS_DP_SDP_CFG2;
}
spd_cfg = dp_read(MMSS_DP_SDP_CFG + sdp_cfg_off);
/* GENERIC1_SDP for SPD Infoframe */
spd_cfg |= BIT(18);
dp_write(MMSS_DP_SDP_CFG + sdp_cfg_off, spd_cfg);
spd_cfg2 = dp_read(MMSS_DP_SDP_CFG2 + sdp_cfg2_off);
/* 28 data bytes for SPD Infoframe with GENERIC1 set */
spd_cfg2 |= BIT(17);
dp_write(MMSS_DP_SDP_CFG2 + sdp_cfg2_off, spd_cfg2);
dp_catalog_panel_sdp_update(panel);
}
static void dp_catalog_get_io_buf(struct dp_catalog_private *catalog)
{
struct dp_parser *parser = catalog->parser;
dp_catalog_fill_io_buf(dp_ahb);
dp_catalog_fill_io_buf(dp_aux);
dp_catalog_fill_io_buf(dp_link);
dp_catalog_fill_io_buf(dp_p0);
dp_catalog_fill_io_buf(dp_phy);
dp_catalog_fill_io_buf(dp_ln_tx0);
dp_catalog_fill_io_buf(dp_ln_tx1);
dp_catalog_fill_io_buf(dp_pll);
dp_catalog_fill_io_buf(usb3_dp_com);
dp_catalog_fill_io_buf(dp_mmss_cc);
dp_catalog_fill_io_buf(hdcp_physical);
dp_catalog_fill_io_buf(dp_p1);
dp_catalog_fill_io_buf(dp_tcsr);
}
static void dp_catalog_get_io(struct dp_catalog_private *catalog)
{
struct dp_parser *parser = catalog->parser;
dp_catalog_fill_io(dp_ahb);
dp_catalog_fill_io(dp_aux);
dp_catalog_fill_io(dp_link);
dp_catalog_fill_io(dp_p0);
dp_catalog_fill_io(dp_phy);
dp_catalog_fill_io(dp_ln_tx0);
dp_catalog_fill_io(dp_ln_tx1);
dp_catalog_fill_io(dp_pll);
dp_catalog_fill_io(usb3_dp_com);
dp_catalog_fill_io(dp_mmss_cc);
dp_catalog_fill_io(hdcp_physical);
dp_catalog_fill_io(dp_p1);
dp_catalog_fill_io(dp_tcsr);
}
static void dp_catalog_set_exe_mode(struct dp_catalog *dp_catalog, char *mode)
{
struct dp_catalog_private *catalog;
if (!dp_catalog) {
DP_ERR("invalid input\n");
return;
}
catalog = container_of(dp_catalog, struct dp_catalog_private,
dp_catalog);
strlcpy(catalog->exe_mode, mode, sizeof(catalog->exe_mode));
if (!strcmp(catalog->exe_mode, "hw"))
catalog->parser->clear_io_buf(catalog->parser);
else
dp_catalog_get_io_buf(catalog);
if (!strcmp(catalog->exe_mode, "hw") ||
!strcmp(catalog->exe_mode, "all")) {
catalog->read = dp_read_hw;
catalog->write = dp_write_hw;
dp_catalog->sub->read = dp_read_sub_hw;
dp_catalog->sub->write = dp_write_sub_hw;
} else {
catalog->read = dp_read_sw;
catalog->write = dp_write_sw;
dp_catalog->sub->read = dp_read_sub_sw;
dp_catalog->sub->write = dp_write_sub_sw;
}
}
static int dp_catalog_init(struct device *dev, struct dp_catalog *dp_catalog,
struct dp_parser *parser)
{
int rc = 0;
struct dp_catalog_private *catalog = container_of(dp_catalog,
struct dp_catalog_private, dp_catalog);
if (parser->hw_cfg.phy_version >= DP_PHY_VERSION_4_2_0)
dp_catalog->sub = dp_catalog_get_v420(dev, dp_catalog, &catalog->io);
else if (parser->hw_cfg.phy_version == DP_PHY_VERSION_2_0_0)
dp_catalog->sub = dp_catalog_get_v200(dev, dp_catalog, &catalog->io);
else
goto end;
if (IS_ERR(dp_catalog->sub)) {
rc = PTR_ERR(dp_catalog->sub);
dp_catalog->sub = NULL;
} else {
dp_catalog->sub->read = dp_read_sub_hw;
dp_catalog->sub->write = dp_write_sub_hw;
}
end:
return rc;
}
void dp_catalog_put(struct dp_catalog *dp_catalog)
{
struct dp_catalog_private *catalog;
if (!dp_catalog)
return;
catalog = container_of(dp_catalog, struct dp_catalog_private,
dp_catalog);
if (dp_catalog->sub && dp_catalog->sub->put)
dp_catalog->sub->put(dp_catalog);
catalog->parser->clear_io_buf(catalog->parser);
devm_kfree(catalog->dev, catalog);
}
struct dp_catalog *dp_catalog_get(struct device *dev, struct dp_parser *parser)
{
int rc = 0;
struct dp_catalog *dp_catalog;
struct dp_catalog_private *catalog;
struct dp_catalog_aux aux = {
.read_data = dp_catalog_aux_read_data,
.write_data = dp_catalog_aux_write_data,
.write_trans = dp_catalog_aux_write_trans,
.clear_trans = dp_catalog_aux_clear_trans,
.reset = dp_catalog_aux_reset,
.update_aux_cfg = dp_catalog_aux_update_cfg,
.enable = dp_catalog_aux_enable,
.setup = dp_catalog_aux_setup,
.get_irq = dp_catalog_aux_get_irq,
.clear_hw_interrupts = dp_catalog_aux_clear_hw_interrupts,
};
struct dp_catalog_ctrl ctrl = {
.state_ctrl = dp_catalog_ctrl_state_ctrl,
.config_ctrl = dp_catalog_ctrl_config_ctrl,
.lane_mapping = dp_catalog_ctrl_lane_mapping,
.lane_pnswap = dp_catalog_ctrl_lane_pnswap,
.mainlink_ctrl = dp_catalog_ctrl_mainlink_ctrl,
.set_pattern = dp_catalog_ctrl_set_pattern,
.reset = dp_catalog_ctrl_reset,
.usb_reset = dp_catalog_ctrl_usb_reset,
.mainlink_ready = dp_catalog_ctrl_mainlink_ready,
.enable_irq = dp_catalog_ctrl_enable_irq,
.phy_reset = dp_catalog_ctrl_phy_reset,
.phy_lane_cfg = dp_catalog_ctrl_phy_lane_cfg,
.update_vx_px = dp_catalog_ctrl_update_vx_px,
.get_interrupt = dp_catalog_ctrl_get_interrupt,
.read_hdcp_status = dp_catalog_ctrl_read_hdcp_status,
.send_phy_pattern = dp_catalog_ctrl_send_phy_pattern,
.read_phy_pattern = dp_catalog_ctrl_read_phy_pattern,
.mst_config = dp_catalog_ctrl_mst_config,
.trigger_act = dp_catalog_ctrl_trigger_act,
.read_act_complete_sts = dp_catalog_ctrl_read_act_complete_sts,
.channel_alloc = dp_catalog_ctrl_channel_alloc,
.update_rg = dp_catalog_ctrl_update_rg,
.channel_dealloc = dp_catalog_ctrl_channel_dealloc,
.fec_config = dp_catalog_ctrl_fec_config,
.mainlink_levels = dp_catalog_ctrl_mainlink_levels,
.late_phy_init = dp_catalog_ctrl_late_phy_init,
.setup_misr = dp_catalog_ctrl_setup_misr,
.read_misr = dp_catalog_ctrl_read_misr,
};
struct dp_catalog_hpd hpd = {
.config_hpd = dp_catalog_hpd_config_hpd,
.get_interrupt = dp_catalog_hpd_get_interrupt,
};
struct dp_catalog_audio audio = {
.init = dp_catalog_audio_init,
.config_acr = dp_catalog_audio_config_acr,
.enable = dp_catalog_audio_enable,
.config_sdp = dp_catalog_audio_config_sdp,
.set_header = dp_catalog_audio_set_header,
.get_header = dp_catalog_audio_get_header,
};
struct dp_catalog_panel panel = {
.timing_cfg = dp_catalog_panel_timing_cfg,
.config_hdr = dp_catalog_panel_config_hdr,
.config_sdp = dp_catalog_panel_config_sdp,
.tpg_config = dp_catalog_panel_tpg_cfg,
.config_spd = dp_catalog_panel_config_spd,
.config_misc = dp_catalog_panel_config_misc,
.set_colorspace = dp_catalog_panel_set_colorspace,
.config_msa = dp_catalog_panel_config_msa,
.update_transfer_unit = dp_catalog_panel_update_transfer_unit,
.config_ctrl = dp_catalog_panel_config_ctrl,
.config_dto = dp_catalog_panel_config_dto,
.dsc_cfg = dp_catalog_panel_dsc_cfg,
.pps_flush = dp_catalog_panel_pps_flush,
.dhdr_flush = dp_catalog_panel_dhdr_flush,
.dhdr_busy = dp_catalog_panel_dhdr_busy,
.get_src_crc = dp_catalog_panel_get_src_crc,
};
if (!dev || !parser) {
DP_ERR("invalid input\n");
rc = -EINVAL;
goto error;
}
catalog = devm_kzalloc(dev, sizeof(*catalog), GFP_KERNEL);
if (!catalog) {
rc = -ENOMEM;
goto error;
}
catalog->dev = dev;
catalog->parser = parser;
catalog->read = dp_read_hw;
catalog->write = dp_write_hw;
dp_catalog_get_io(catalog);
strlcpy(catalog->exe_mode, "hw", sizeof(catalog->exe_mode));
dp_catalog = &catalog->dp_catalog;
dp_catalog->aux = aux;
dp_catalog->ctrl = ctrl;
dp_catalog->hpd = hpd;
dp_catalog->audio = audio;
dp_catalog->panel = panel;
rc = dp_catalog_init(dev, dp_catalog, parser);
if (rc) {
dp_catalog_put(dp_catalog);
goto error;
}
dp_catalog->set_exe_mode = dp_catalog_set_exe_mode;
dp_catalog->get_reg_dump = dp_catalog_reg_dump;
return dp_catalog;
error:
return ERR_PTR(rc);
}