Merge "mdss: DisplayPort: update link-training settings and do DP reset"

This commit is contained in:
Linux Build Service Account 2016-10-10 18:28:15 -07:00 committed by Gerrit - the friendly Code Review server
commit 31e788f9f2
9 changed files with 222 additions and 44 deletions

View file

@ -18,6 +18,7 @@
#include <linux/iopoll.h>
#include <linux/delay.h>
#include <linux/clk/msm-clock-generic.h>
#include <linux/usb/usbpd.h>
#include "mdss-pll.h"
#include "mdss-dp-pll.h"
@ -172,9 +173,27 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
{
u32 res = 0;
struct mdss_pll_resources *dp_res = vco->priv;
u8 orientation, ln_cnt;
u32 spare_value;
spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
ln_cnt = spare_value & 0x0F;
orientation = (spare_value & 0xF0) >> 4;
pr_debug("%s: spare_value=0x%x, ln_cnt=0x%x, orientation=0x%x\n",
__func__, spare_value, ln_cnt, orientation);
if (ln_cnt != 4) {
if (orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_PD_CTL, 0x2d);
else
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_PD_CTL, 0x35);
} else {
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_PD_CTL, 0x3d);
}
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_PD_CTL, 0x3d);
/* Make sure the PHY register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->pll_base,
@ -314,8 +333,13 @@ int dp_config_vco_rate(struct dp_pll_vco_clk *vco, unsigned long rate)
/* Make sure the PLL register writes are done */
wmb();
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_MODE, 0x58);
if (orientation == ORIENTATION_CC2)
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_MODE, 0x48);
else
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_MODE, 0x58);
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_TX0_TX1_LANE_CTL, 0x05);
MDSS_PLL_REG_W(dp_res->phy_base,
@ -427,6 +451,12 @@ static int dp_pll_enable(struct clk *c)
u32 status;
struct dp_pll_vco_clk *vco = mdss_dp_to_vco_clk(c);
struct mdss_pll_resources *dp_res = vco->priv;
u8 orientation, ln_cnt;
u32 spare_value, bias_en, drvr_en;
spare_value = MDSS_PLL_REG_R(dp_res->phy_base, DP_PHY_SPARE0);
ln_cnt = spare_value & 0x0F;
orientation = (spare_value & 0xF0) >> 4;
MDSS_PLL_REG_W(dp_res->phy_base,
DP_PHY_CFG, 0x01);
@ -474,18 +504,45 @@ static int dp_pll_enable(struct clk *c)
pr_debug("%s: PLL is locked\n", __func__);
MDSS_PLL_REG_W(dp_res->phy_base,
if (ln_cnt == 1) {
bias_en = 0x3e;
drvr_en = 0x13;
} else {
bias_en = 0x3f;
drvr_en = 0x10;
}
if (ln_cnt != 4) {
if (orientation == ORIENTATION_CC1) {
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN,
0x3f);
MDSS_PLL_REG_W(dp_res->phy_base,
bias_en);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN,
0x10);
MDSS_PLL_REG_W(dp_res->phy_base,
drvr_en);
} else {
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN,
0x3f);
MDSS_PLL_REG_W(dp_res->phy_base,
bias_en);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN,
0x10);
drvr_en);
}
} else {
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TRANSCEIVER_BIAS_EN,
bias_en);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_HIGHZ_DRVR_EN,
drvr_en);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_TRANSCEIVER_BIAS_EN,
bias_en);
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX1_OFFSET + TXn_HIGHZ_DRVR_EN,
drvr_en);
}
MDSS_PLL_REG_W(dp_res->phy_base,
QSERDES_TX0_OFFSET + TXn_TX_POL_INV,
0x0a);
@ -615,7 +672,7 @@ int dp_vco_prepare(struct clk *c)
rc = dp_pll_enable(c);
if (rc) {
mdss_pll_resource_enable(dp_pll_res, false);
pr_err("ndx=%d failed to enable dsi pll\n",
pr_err("ndx=%d failed to enable dp pll\n",
dp_pll_res->index);
goto error;
}

View file

@ -41,6 +41,7 @@
#define DP_PHY_TX0_TX1_LANE_CTL 0x0068
#define DP_PHY_TX2_TX3_LANE_CTL 0x0084
#define DP_PHY_SPARE0 0x00A8
#define DP_PHY_STATUS 0x00BC
/* Tx registers */

View file

@ -789,17 +789,34 @@ void mdss_dp_config_ctrl(struct mdss_dp_drv_pdata *dp)
cap = &dp->dpcd;
data = dp->lane_cnt - 1;
data <<= 4;
data |= (2 << 13); /* Default-> LSCLK DIV: 1/4 LCLK */
/* Color Format */
switch (dp->panel_data.panel_info.out_format) {
case MDP_Y_CBCR_H2V2:
data |= (1 << 11); /* YUV420 */
break;
case MDP_Y_CBCR_H2V1:
data |= (2 << 11); /* YUV422 */
break;
default:
data |= (0 << 11); /* RGB */
break;
}
/* Scrambler reset enable */
if (cap->scrambler_reset)
data |= (1 << 10);
if (dp->edid.color_depth != 6)
data |= 0x100; /* Default: 8 bits */
/* Num of Lanes */
data |= ((dp->lane_cnt - 1) << 4);
if (cap->enhanced_frame)
data |= 0x40;
if (dp->edid.color_depth == 8) {
/* 0 == 6 bits, 1 == 8 bits */
data |= 0x100; /* bit 8 */
}
if (!timing->interlaced) /* progressive */
data |= 0x04;
@ -863,6 +880,8 @@ static int dp_audio_info_setup(struct platform_device *pdev,
mdss_dp_set_safe_to_exit_level(&dp_ctrl->ctrl_io, dp_ctrl->lane_cnt);
mdss_dp_audio_enable(&dp_ctrl->ctrl_io, true);
dp_ctrl->wait_for_audio_comp = true;
return rc;
} /* dp_audio_info_setup */
@ -885,6 +904,17 @@ static int dp_get_audio_edid_blk(struct platform_device *pdev,
return rc;
} /* dp_get_audio_edid_blk */
static void dp_audio_codec_teardown_done(struct platform_device *pdev)
{
struct mdss_dp_drv_pdata *dp = platform_get_drvdata(pdev);
if (!dp)
pr_err("invalid input\n");
pr_debug("audio codec teardown done\n");
complete_all(&dp->audio_comp);
}
static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
{
int ret = 0;
@ -906,6 +936,8 @@ static int mdss_dp_init_ext_disp(struct mdss_dp_drv_pdata *dp)
dp_get_audio_edid_blk;
dp->ext_audio_data.codec_ops.cable_status =
dp_get_cable_status;
dp->ext_audio_data.codec_ops.teardown_done =
dp_audio_codec_teardown_done;
if (!dp->pdev->dev.of_node) {
pr_err("%s cannot find dp dev.of_node\n", __func__);
@ -987,6 +1019,13 @@ static int dp_init_panel_info(struct mdss_dp_drv_pdata *dp_drv, u32 vic)
return 0;
} /* dp_init_panel_info */
static inline void mdss_dp_set_audio_switch_node(
struct mdss_dp_drv_pdata *dp, int val)
{
if (dp && dp->ext_audio_data.intf_ops.notify)
dp->ext_audio_data.intf_ops.notify(dp->ext_pdev,
val);
}
int mdss_dp_on(struct mdss_panel_data *pdata)
{
@ -1054,6 +1093,9 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
goto exit;
}
mdss_dp_phy_share_lane_config(&dp_drv->phy_io,
orientation, dp_drv->dpcd.max_lane_count);
pr_debug("link_rate = 0x%x\n", dp_drv->link_rate);
dp_drv->power_data[DP_CTRL_PM].clk_config[0].rate =
@ -1096,6 +1138,7 @@ int mdss_dp_on(struct mdss_panel_data *pdata)
pr_debug("mainlink ready\n");
dp_drv->power_on = true;
mdss_dp_set_audio_switch_node(dp_drv, true);
pr_debug("End-\n");
exit:
@ -1119,14 +1162,15 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
mutex_lock(&dp_drv->train_mutex);
reinit_completion(&dp_drv->idle_comp);
mdss_dp_state_ctrl(&dp_drv->ctrl_io, 0);
mdss_dp_state_ctrl(&dp_drv->ctrl_io, ST_PUSH_IDLE);
if (dp_drv->link_clks_on)
mdss_dp_mainlink_ctrl(&dp_drv->ctrl_io, false);
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, false);
mdss_dp_audio_enable(&dp_drv->ctrl_io, false);
mdss_dp_irq_disable(dp_drv);
mdss_dp_config_gpios(dp_drv, false);
@ -1147,14 +1191,6 @@ int mdss_dp_off(struct mdss_panel_data *pdata)
return 0;
}
static inline void mdss_dp_set_audio_switch_node(
struct mdss_dp_drv_pdata *dp, int val)
{
if (dp && dp->ext_audio_data.intf_ops.notify)
dp->ext_audio_data.intf_ops.notify(dp->ext_pdev,
val);
}
static void mdss_dp_send_cable_notification(
struct mdss_dp_drv_pdata *dp, int val)
{
@ -1169,6 +1205,38 @@ static void mdss_dp_send_cable_notification(
dp->ext_audio_data.type, val);
}
static void mdss_dp_audio_codec_wait(struct mdss_dp_drv_pdata *dp)
{
const int audio_completion_timeout_ms = HZ * 3;
int ret = 0;
if (!dp->wait_for_audio_comp)
return;
reinit_completion(&dp->audio_comp);
ret = wait_for_completion_timeout(&dp->audio_comp,
audio_completion_timeout_ms);
if (ret <= 0)
pr_warn("audio codec teardown timed out\n");
dp->wait_for_audio_comp = false;
}
static void mdss_dp_notify_clients(struct mdss_dp_drv_pdata *dp, bool enable)
{
if (enable) {
mdss_dp_send_cable_notification(dp, enable);
} else {
mdss_dp_set_audio_switch_node(dp, enable);
mdss_dp_audio_codec_wait(dp);
mdss_dp_send_cable_notification(dp, enable);
}
pr_debug("notify state %s done\n",
enable ? "ENABLE" : "DISABLE");
}
static int mdss_dp_edid_init(struct mdss_panel_data *pdata)
{
struct mdss_dp_drv_pdata *dp_drv = NULL;
@ -1236,15 +1304,19 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
mdss_dp_aux_init(dp_drv);
mdss_dp_phy_initialize(dp_drv);
mdss_dp_ctrl_reset(&dp_drv->ctrl_io);
mdss_dp_phy_reset(&dp_drv->ctrl_io);
mdss_dp_aux_reset(&dp_drv->ctrl_io);
mdss_dp_phy_initialize(dp_drv);
mdss_dp_aux_ctrl(&dp_drv->ctrl_io, true);
pr_debug("Ctrl_hw_rev =0x%x, phy hw_rev =0x%x\n",
mdss_dp_get_ctrl_hw_version(&dp_drv->ctrl_io),
mdss_dp_get_phy_hw_version(&dp_drv->phy_io));
pr_debug("plug Orientation = %d\n",
usbpd_get_plug_orientation(dp_drv->pd));
mdss_dp_phy_aux_setup(&dp_drv->phy_io);
mdss_dp_irq_enable(dp_drv);
@ -1264,8 +1336,7 @@ static int mdss_dp_host_init(struct mdss_panel_data *pdata)
goto edid_error;
}
mdss_dp_send_cable_notification(dp_drv, true);
mdss_dp_set_audio_switch_node(dp_drv, true);
mdss_dp_notify_clients(dp_drv, true);
dp_drv->dp_initialized = true;
return ret;
@ -1771,8 +1842,7 @@ static void dp_send_events(struct mdss_dp_drv_pdata *dp, u32 events)
{
spin_lock(&dp->event_lock);
dp->current_event = events;
queue_delayed_work(dp->workq,
&dp->dwork, HZ);
queue_delayed_work(dp->workq, &dp->dwork, HZ / 100);
spin_unlock(&dp->event_lock);
}
@ -1883,8 +1953,7 @@ static void usbpd_disconnect_callback(struct usbpd_svid_handler *hdlr)
mutex_lock(&dp_drv->pd_msg_mutex);
dp_drv->cable_connected = false;
mutex_unlock(&dp_drv->pd_msg_mutex);
mdss_dp_send_cable_notification(dp_drv, false);
mdss_dp_set_audio_switch_node(dp_drv, false);
mdss_dp_notify_clients(dp_drv, false);
}
static void usbpd_response_callback(struct usbpd_svid_handler *hdlr, u8 cmd,
@ -2135,6 +2204,8 @@ static int mdss_dp_probe(struct platform_device *pdev)
mdss_dp_device_register(dp_drv);
dp_drv->inited = true;
dp_drv->wait_for_audio_comp = false;
init_completion(&dp_drv->audio_comp);
pr_debug("done\n");

View file

@ -399,6 +399,7 @@ struct mdss_dp_drv_pdata {
struct completion train_comp;
struct completion idle_comp;
struct completion video_comp;
struct completion audio_comp;
struct mutex aux_mutex;
struct mutex train_mutex;
struct mutex pd_msg_mutex;
@ -423,6 +424,7 @@ struct mdss_dp_drv_pdata {
char delay_start;
u32 bpp;
struct dp_statistic dp_stat;
bool wait_for_audio_comp;
/* event */
struct workqueue_struct *workq;

View file

@ -1113,17 +1113,17 @@ static void dp_host_train_set(struct mdss_dp_drv_pdata *ep, int train)
}
char vm_pre_emphasis[4][4] = {
{0x00, 0x06, 0x09, 0x0C}, /* pe0, 0 db */
{0x00, 0x06, 0x09, 0xFF}, /* pe1, 3.5 db */
{0x03, 0x06, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0x03, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
{0x00, 0x09, 0x11, 0x0C}, /* pe0, 0 db */
{0x00, 0x0A, 0x10, 0xFF}, /* pe1, 3.5 db */
{0x00, 0x0C, 0xFF, 0xFF}, /* pe2, 6.0 db */
{0x00, 0xFF, 0xFF, 0xFF} /* pe3, 9.5 db */
};
/* voltage swing, 0.2v and 1.0v are not support */
char vm_voltage_swing[4][4] = {
{0x0a, 0x18, 0x1A, 0x1E}, /* sw0, 0.4v */
{0x07, 0x1A, 0x1E, 0xFF}, /* sw1, 0.6 v */
{0x1A, 0x1E, 0xFF, 0xFF}, /* sw1, 0.8 v */
{0x07, 0x0f, 0x12, 0x1E}, /* sw0, 0.4v */
{0x11, 0x1D, 0x1F, 0xFF}, /* sw1, 0.6 v */
{0x18, 0x1F, 0xFF, 0xFF}, /* sw1, 0.8 v */
{0x1E, 0xFF, 0xFF, 0xFF} /* sw1, 1.2 v, optional */
};

View file

@ -143,6 +143,18 @@ void mdss_dp_aux_reset(struct dss_io_data *ctrl_io)
writel_relaxed(aux_ctrl, ctrl_io->base + DP_AUX_CTRL);
}
/* reset DP controller */
void mdss_dp_ctrl_reset(struct dss_io_data *ctrl_io)
{
u32 sw_reset = readl_relaxed(ctrl_io->base + DP_SW_RESET);
sw_reset |= BIT(0);
writel_relaxed(sw_reset, ctrl_io->base + DP_SW_RESET);
udelay(1000);
sw_reset &= ~BIT(0);
writel_relaxed(sw_reset, ctrl_io->base + DP_SW_RESET);
}
/* reset DP Mainlink */
void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io)
{
@ -441,6 +453,17 @@ u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp)
return config;
}
void mdss_dp_phy_share_lane_config(struct dss_io_data *phy_io,
u8 orientation, u8 ln_cnt)
{
u32 info = 0x0;
info |= (ln_cnt & 0x0F);
info |= ((orientation & 0x0F) << 4);
pr_debug("Shared Info = 0x%x\n", info);
writel_relaxed(info, phy_io->base + DP_PHY_SPARE0);
}
void mdss_dp_config_audio_acr_ctrl(struct dss_io_data *ctrl_io, char link_rate)
{
u32 acr_ctrl = 0;

View file

@ -150,6 +150,8 @@
#define DP_PHY_AUX_INTERRUPT_MASK (0x00000044)
#define DP_PHY_AUX_INTERRUPT_CLEAR (0x00000048)
#define DP_PHY_SPARE0 0x00A8
#define QSERDES_TX0_OFFSET 0x0400
#define QSERDES_TX1_OFFSET 0x0800
@ -205,6 +207,7 @@ int dp_aux_write(void *ep, struct edp_cmd *cmd);
void mdss_dp_state_ctrl(struct dss_io_data *ctrl_io, u32 data);
u32 mdss_dp_get_ctrl_hw_version(struct dss_io_data *ctrl_io);
u32 mdss_dp_get_phy_hw_version(struct dss_io_data *phy_io);
void mdss_dp_ctrl_reset(struct dss_io_data *ctrl_io);
void mdss_dp_aux_reset(struct dss_io_data *ctrl_io);
void mdss_dp_mainlink_reset(struct dss_io_data *ctrl_io);
void mdss_dp_phy_reset(struct dss_io_data *ctrl_io);
@ -231,6 +234,8 @@ void mdss_dp_usbpd_ext_dp_status(struct usbpd_dp_status *dp_status);
u32 mdss_dp_usbpd_gen_config_pkt(struct mdss_dp_drv_pdata *dp);
void mdss_dp_ctrl_lane_mapping(struct dss_io_data *ctrl_io,
struct lane_mapping l_map);
void mdss_dp_phy_share_lane_config(struct dss_io_data *phy_io,
u8 orientation, u8 ln_cnt);
void mdss_dp_config_audio_acr_ctrl(struct dss_io_data *ctrl_io,
char link_rate);
void mdss_dp_audio_setup_sdps(struct dss_io_data *ctrl_io);

View file

@ -365,6 +365,7 @@ static int msm_ext_disp_hpd(struct platform_device *pdev,
ext_disp->ops->get_audio_edid_blk = NULL;
ext_disp->ops->cable_status = NULL;
ext_disp->ops->get_intf_id = NULL;
ext_disp->ops->teardown_done = NULL;
}
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;
@ -463,6 +464,20 @@ end:
return ret;
}
static void msm_ext_disp_teardown_done(struct platform_device *pdev)
{
int ret = 0;
struct msm_ext_disp_init_data *data = NULL;
ret = msm_ext_disp_get_intf_data_helper(pdev, &data);
if (ret || !data) {
pr_err("invalid input");
return;
}
data->codec_ops.teardown_done(data->pdev);
}
static int msm_ext_disp_get_intf_id(struct platform_device *pdev)
{
int ret = 0;
@ -545,6 +560,8 @@ static int msm_ext_disp_notify(struct platform_device *pdev,
msm_ext_disp_cable_status;
ext_disp->ops->get_intf_id =
msm_ext_disp_get_intf_id;
ext_disp->ops->teardown_done =
msm_ext_disp_teardown_done;
}
switch_set_state(&ext_disp->audio_sdev, (int)new_state);
@ -614,6 +631,7 @@ static int msm_ext_disp_audio_ack(struct platform_device *pdev, u32 ack)
ext_disp->ops->get_audio_edid_blk = NULL;
ext_disp->ops->cable_status = NULL;
ext_disp->ops->get_intf_id = NULL;
ext_disp->ops->teardown_done = NULL;
}
ext_disp->current_disp = EXT_DISPLAY_TYPE_MAX;

View file

@ -108,6 +108,7 @@ struct msm_ext_disp_audio_codec_ops {
struct msm_ext_disp_audio_edid_blk *blk);
int (*cable_status)(struct platform_device *pdev, u32 vote);
int (*get_intf_id)(struct platform_device *pdev);
void (*teardown_done)(struct platform_device *pdev);
};
/*