
Currently, for every DP hardware register read/write, there is a string comparison to determine the execution mode. This adds up an extra delay while powering up/down which does a large number of register reads and writes. During stress testing and automation, this can cause an issue resulting in failures. Remove the unnecessary delays by using common APIs for register reads and writes. Switch these APIs only in case of execution mode change. Change-Id: I9403873a29b3466c606297b2aa386d0885bb2dc7 Signed-off-by: Ajay Singh Parmar <aparmar@codeaurora.org>
273 řádky
6.5 KiB
C
273 řádky
6.5 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2012-2019, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/delay.h>
|
|
|
|
#include "dp_catalog.h"
|
|
#include "dp_reg.h"
|
|
#include "dp_debug.h"
|
|
|
|
#define dp_catalog_get_priv_v200(x) ({ \
|
|
struct dp_catalog *catalog; \
|
|
catalog = container_of(x, struct dp_catalog, x); \
|
|
container_of(catalog->sub, \
|
|
struct dp_catalog_private_v200, sub); \
|
|
})
|
|
|
|
#define dp_read(x) ({ \
|
|
catalog->sub.read(catalog->dpc, io_data, x); \
|
|
})
|
|
|
|
#define dp_write(x, y) ({ \
|
|
catalog->sub.write(catalog->dpc, io_data, x, y); \
|
|
})
|
|
|
|
struct dp_catalog_private_v200 {
|
|
struct device *dev;
|
|
struct dp_catalog_io *io;
|
|
struct dp_catalog *dpc;
|
|
struct dp_catalog_sub sub;
|
|
};
|
|
|
|
static void dp_catalog_aux_clear_hw_int_v200(struct dp_catalog_aux *aux)
|
|
{
|
|
struct dp_catalog_private_v200 *catalog;
|
|
struct dp_io_data *io_data;
|
|
u32 data = 0;
|
|
|
|
if (!aux) {
|
|
DP_ERR("invalid input\n");
|
|
return;
|
|
}
|
|
|
|
catalog = dp_catalog_get_priv_v200(aux);
|
|
io_data = catalog->io->dp_phy;
|
|
|
|
data = dp_read(DP_PHY_AUX_INTERRUPT_STATUS_V200);
|
|
|
|
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x1f);
|
|
wmb(); /* make sure 0x1f is written before next write */
|
|
|
|
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0x9f);
|
|
wmb(); /* make sure 0x9f is written before next write */
|
|
|
|
dp_write(DP_PHY_AUX_INTERRUPT_CLEAR_V200, 0);
|
|
wmb(); /* make sure register is cleared */
|
|
}
|
|
|
|
static void dp_catalog_aux_setup_v200(struct dp_catalog_aux *aux,
|
|
struct dp_aux_cfg *cfg)
|
|
{
|
|
struct dp_catalog_private_v200 *catalog;
|
|
struct dp_io_data *io_data;
|
|
int i = 0, sw_reset = 0;
|
|
|
|
if (!aux || !cfg) {
|
|
DP_ERR("invalid input\n");
|
|
return;
|
|
}
|
|
|
|
catalog = dp_catalog_get_priv_v200(aux);
|
|
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);
|
|
|
|
dp_write(DP_PHY_CTRL, 0x4); /* bit 2 */
|
|
udelay(1000);
|
|
dp_write(DP_PHY_CTRL, 0x0); /* bit 2 */
|
|
wmb(); /* make sure programming happened */
|
|
|
|
io_data = catalog->io->dp_tcsr;
|
|
dp_write(0x4c, 0x1); /* bit 0 & 2 */
|
|
wmb(); /* make sure programming happened */
|
|
|
|
io_data = catalog->io->dp_phy;
|
|
dp_write(DP_PHY_PD_CTL, 0x3c);
|
|
wmb(); /* make sure PD programming happened */
|
|
dp_write(DP_PHY_PD_CTL, 0x3d);
|
|
wmb(); /* make sure PD programming happened */
|
|
|
|
/* 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_V200, 0x1F);
|
|
wmb(); /* make sure AUX configuration is done before enabling it */
|
|
}
|
|
|
|
static void dp_catalog_panel_config_msa_v200(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_v200 *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_v200(panel);
|
|
io_data = catalog->io->dp_mmss_cc;
|
|
|
|
if (panel->stream_id == DP_STREAM_1)
|
|
strm_reg_off = MMSS_DP_PIXEL1_M_V200 -
|
|
MMSS_DP_PIXEL_M_V200;
|
|
|
|
pixel_m = dp_read(MMSS_DP_PIXEL_M_V200 + strm_reg_off);
|
|
pixel_n = dp_read(MMSS_DP_PIXEL_N_V200 + 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_lane_mapping_v200(struct dp_catalog_ctrl *ctrl,
|
|
bool flipped, char *lane_map)
|
|
{
|
|
struct dp_catalog_private_v200 *catalog;
|
|
struct dp_io_data *io_data;
|
|
u8 l_map[4] = { 0 }, i = 0, j = 0;
|
|
u32 lane_map_reg = 0;
|
|
|
|
if (!ctrl) {
|
|
DP_ERR("invalid input\n");
|
|
return;
|
|
}
|
|
|
|
catalog = dp_catalog_get_priv_v200(ctrl);
|
|
io_data = catalog->io->dp_link;
|
|
|
|
/* For flip case, swap phy lanes with ML0 and ML3, ML1 and ML2 */
|
|
if (flipped) {
|
|
for (i = 0; i < DP_MAX_PHY_LN; i++) {
|
|
if (lane_map[i] == DP_ML0) {
|
|
for (j = 0; j < DP_MAX_PHY_LN; j++) {
|
|
if (lane_map[j] == DP_ML3) {
|
|
l_map[i] = DP_ML3;
|
|
l_map[j] = DP_ML0;
|
|
break;
|
|
}
|
|
}
|
|
} else if (lane_map[i] == DP_ML1) {
|
|
for (j = 0; j < DP_MAX_PHY_LN; j++) {
|
|
if (lane_map[j] == DP_ML2) {
|
|
l_map[i] = DP_ML2;
|
|
l_map[j] = DP_ML1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* Normal orientation */
|
|
for (i = 0; i < DP_MAX_PHY_LN; i++)
|
|
l_map[i] = lane_map[i];
|
|
}
|
|
|
|
lane_map_reg = ((l_map[3]&3)<<6)|((l_map[2]&3)<<4)|((l_map[1]&3)<<2)
|
|
|(l_map[0]&3);
|
|
|
|
dp_write(DP_LOGICAL2PHYSICAL_LANE_MAPPING, lane_map_reg);
|
|
}
|
|
|
|
static void dp_catalog_ctrl_usb_reset_v200(struct dp_catalog_ctrl *ctrl,
|
|
bool flip)
|
|
{
|
|
}
|
|
|
|
static void dp_catalog_put_v200(struct dp_catalog *catalog)
|
|
{
|
|
struct dp_catalog_private_v200 *catalog_priv;
|
|
|
|
if (!catalog)
|
|
return;
|
|
|
|
catalog_priv = container_of(catalog->sub,
|
|
struct dp_catalog_private_v200, sub);
|
|
|
|
devm_kfree(catalog_priv->dev, catalog_priv);
|
|
}
|
|
|
|
struct dp_catalog_sub *dp_catalog_get_v200(struct device *dev,
|
|
struct dp_catalog *catalog, struct dp_catalog_io *io)
|
|
{
|
|
struct dp_catalog_private_v200 *catalog_priv;
|
|
|
|
if (!dev || !catalog) {
|
|
DP_ERR("invalid input\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
catalog_priv = devm_kzalloc(dev, sizeof(*catalog_priv), GFP_KERNEL);
|
|
if (!catalog_priv)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
catalog_priv->dev = dev;
|
|
catalog_priv->io = io;
|
|
catalog_priv->dpc = catalog;
|
|
|
|
catalog_priv->sub.put = dp_catalog_put_v200;
|
|
|
|
catalog->aux.clear_hw_interrupts = dp_catalog_aux_clear_hw_int_v200;
|
|
catalog->aux.setup = dp_catalog_aux_setup_v200;
|
|
|
|
catalog->panel.config_msa = dp_catalog_panel_config_msa_v200;
|
|
|
|
catalog->ctrl.lane_mapping = dp_catalog_ctrl_lane_mapping_v200;
|
|
catalog->ctrl.usb_reset = dp_catalog_ctrl_usb_reset_v200;
|
|
|
|
return &catalog_priv->sub;
|
|
}
|