blob: 6449e02dab9a98b6723d56b7653338152a582619 [file] [log] [blame]
/* Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
*/
#include <debug.h>
#include <reg.h>
#include <mipi_dsi.h>
#include <platform/iomap.h>
#if (DISPLAY_TYPE_MDSS == 0)
#define MIPI_DSI0_BASE MIPI_DSI_BASE
#define MIPI_DSI1_BASE MIPI_DSI_BASE
#endif
static void mipi_dsi_calibration(void)
{
uint32_t i = 0;
uint32_t term_cnt = 5000;
int32_t cal_busy = readl(MIPI_DSI_BASE + 0x550);
/* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */
writel(0x01, MIPI_DSI_BASE + 0x0518);
/* DSI1_DSIPHY_CAL_SW_CFG2 */
writel(0x0, MIPI_DSI_BASE + 0x0534);
/* DSI1_DSIPHY_CAL_HW_CFG1 */
writel(0x5a, MIPI_DSI_BASE + 0x053c);
/* DSI1_DSIPHY_CAL_HW_CFG3 */
writel(0x10, MIPI_DSI_BASE + 0x0544);
/* DSI1_DSIPHY_CAL_HW_CFG4 */
writel(0x01, MIPI_DSI_BASE + 0x0548);
/* DSI1_DSIPHY_CAL_HW_CFG0 */
writel(0x01, MIPI_DSI_BASE + 0x0538);
/* DSI1_DSIPHY_CAL_HW_TRIGGER */
writel(0x01, MIPI_DSI_BASE + 0x0528);
/* DSI1_DSIPHY_CAL_HW_TRIGGER */
writel(0x00, MIPI_DSI_BASE + 0x0528);
cal_busy = readl(MIPI_DSI_BASE + 0x550);
while (cal_busy & 0x10) {
i++;
if (i > term_cnt) {
dprintf(CRITICAL, "DSI1 PHY REGULATOR NOT READY,"
"exceeded polling TIMEOUT!\n");
break;
}
cal_busy = readl(MIPI_DSI_BASE + 0x550);
}
}
int mipi_dsi_phy_init(struct mipi_dsi_panel_config *pinfo)
{
struct mipi_dsi_phy_ctrl *pd;
uint32_t i, off = 0;
int mdp_rev;
mdp_rev = mdp_get_revision();
if (MDP_REV_303 == mdp_rev || MDP_REV_41 == mdp_rev) {
writel(0x00000001, DSIPHY_SW_RESET);
writel(0x00000000, DSIPHY_SW_RESET);
pd = (pinfo->dsi_phy_config);
off = 0x02cc; /* regulator ctrl 0 */
for (i = 0; i < 4; i++) {
writel(pd->regulator[i], MIPI_DSI_BASE + off);
off += 4;
}
off = 0x0260; /* phy timig ctrl 0 */
for (i = 0; i < 11; i++) {
writel(pd->timing[i], MIPI_DSI_BASE + off);
off += 4;
}
/* T_CLK_POST, T_CLK_PRE for CLK lane P/N HS 200 mV timing
length should > data lane HS timing length */
writel(0xa1e, DSI_CLKOUT_TIMING_CTRL);
off = 0x0290; /* ctrl 0 */
for (i = 0; i < 4; i++) {
writel(pd->ctrl[i], MIPI_DSI_BASE + off);
off += 4;
}
off = 0x02a0; /* strength 0 */
for (i = 0; i < 4; i++) {
writel(pd->strength[i], MIPI_DSI_BASE + off);
off += 4;
}
if (1 == pinfo->num_of_lanes)
pd->pll[10] |= 0x8;
off = 0x0204; /* pll ctrl 1, skip 0 */
for (i = 1; i < 21; i++) {
writel(pd->pll[i], MIPI_DSI_BASE + off);
off += 4;
}
/* pll ctrl 0 */
writel(pd->pll[0], MIPI_DSI_BASE + 0x200);
writel((pd->pll[0] | 0x01), MIPI_DSI_BASE + 0x200);
/* lane swp ctrol */
if (pinfo->lane_swap)
writel(pinfo->lane_swap, MIPI_DSI_BASE + 0xac);
} else {
writel(0x0001, MIPI_DSI_BASE + 0x128); /* start phy sw reset */
writel(0x0000, MIPI_DSI_BASE + 0x128); /* end phy w reset */
writel(0x0003, MIPI_DSI_BASE + 0x500); /* regulator_ctrl_0 */
writel(0x0001, MIPI_DSI_BASE + 0x504); /* regulator_ctrl_1 */
writel(0x0001, MIPI_DSI_BASE + 0x508); /* regulator_ctrl_2 */
writel(0x0000, MIPI_DSI_BASE + 0x50c); /* regulator_ctrl_3 */
writel(0x0100, MIPI_DSI_BASE + 0x510); /* regulator_ctrl_4 */
pd = (pinfo->dsi_phy_config);
off = 0x0480; /* strength 0 - 2 */
for (i = 0; i < 3; i++) {
writel(pd->strength[i], MIPI_DSI_BASE + off);
off += 4;
}
off = 0x0470; /* ctrl 0 - 3 */
for (i = 0; i < 4; i++) {
writel(pd->ctrl[i], MIPI_DSI_BASE + off);
off += 4;
}
off = 0x0500; /* regulator ctrl 0 - 4 */
for (i = 0; i < 5; i++) {
writel(pd->regulator[i], MIPI_DSI_BASE + off);
off += 4;
}
mipi_dsi_calibration();
off = 0x0204; /* pll ctrl 1 - 19, skip 0 */
for (i = 1; i < 20; i++) {
writel(pd->pll[i], MIPI_DSI_BASE + off);
off += 4;
}
/* pll ctrl 0 */
writel(pd->pll[0], MIPI_DSI_BASE + 0x200);
writel((pd->pll[0] | 0x01), MIPI_DSI_BASE + 0x200);
/* Check that PHY is ready */
while (!(readl(DSIPHY_PLL_RDY) & 0x01))
udelay(1);
writel(0x202D, DSI_CLKOUT_TIMING_CTRL);
off = 0x0440; /* phy timing ctrl 0 - 11 */
for (i = 0; i < 12; i++) {
writel(pd->timing[i], MIPI_DSI_BASE + off);
off += 4;
}
}
return 0;
}
void mdss_dsi_phy_sw_reset(uint32_t ctl_base)
{
/* start phy sw reset */
writel(0x0001, ctl_base + 0x012c);
udelay(1000);
/* end phy sw reset */
writel(0x0000, ctl_base + 0x012c);
udelay(100);
}
void mdss_dsi_uniphy_pll_lock_detect_setting(uint32_t ctl_base)
{
writel(0x0d, ctl_base + 0x0264); /* LKDetect CFG2 */
writel(0x0c, ctl_base + 0x0264); /* LKDetect CFG2 */
udelay(1);
writel(0x0d, ctl_base + 0x0264); /* LKDetect CFG2 */
}
void mdss_dsi_uniphy_pll_sw_reset(uint32_t ctl_base)
{
writel(0x01, ctl_base + 0x0268); /* PLL TEST CFG */
udelay(1);
writel(0x00, ctl_base + 0x0268); /* PLL TEST CFG */
udelay(1);
}
int mdss_dsi_phy_regulator_init(struct mdss_dsi_phy_ctrl *pd)
{
/* DSI0 and DSI1 have a common regulator */
uint32_t off = 0x0580; /* phy regulator ctrl settings */
/* Regulator ctrl 0 */
writel(0x00, MIPI_DSI0_BASE + off + (4 * 0));
/* Regulator ctrl - CAL_PWD_CFG */
writel(pd->regulator[6], MIPI_DSI0_BASE + off + (4 * 6));
/* Regulator ctrl - TEST */
writel(pd->regulator[5], MIPI_DSI0_BASE + off + (4 * 5));
/* Regulator ctrl 3 */
writel(pd->regulator[3], MIPI_DSI0_BASE + off + (4 * 3));
/* Regulator ctrl 2 */
writel(pd->regulator[2], MIPI_DSI0_BASE + off + (4 * 2));
/* Regulator ctrl 1 */
writel(pd->regulator[1], MIPI_DSI0_BASE + off + (4 * 1));
/* Regulator ctrl 0 */
writel(pd->regulator[0], MIPI_DSI0_BASE + off + (4 * 0));
/* Regulator ctrl 4 */
writel(pd->regulator[4], MIPI_DSI0_BASE + off + (4 * 4));
dmb();
}
int mdss_dsi_v2_phy_init(struct mipi_dsi_panel_config *pinfo, uint32_t ctl_base)
{
struct mdss_dsi_phy_ctrl *pd;
uint32_t i, ln, off = 0, offset;
pd = pinfo->mdss_dsi_phy_config;
/* DSI PHY configuration */
off = 0x480;
writel(pd->strength[0], ctl_base + off + (4 * 0));
writel(pd->strength[1], ctl_base + off + (4 * 2));
off = 0x470;
writel(0x10, ctl_base + off + (4 * 3));
writel(0x5F, ctl_base + off + (4 * 0));
off = 0x500;
/* use LDO mode */
writel(0x25, ctl_base + 0x4B0);
for (i = 0; i < 5; i++)
writel(pd->regulator[i], ctl_base + off + (4 * i));
mipi_dsi_calibration();
/* 4 lanes + clk lane configuration */
/* lane config n * (0 - 4) & DataPath setup */
for (ln = 0; ln < 5; ln++) {
off = 0x0300 + (ln * 0x40);
for (i = 0; i < 9; i++) {
offset = i + (ln * 9);
writel(pd->laneCfg[offset], ctl_base + off);
dmb();
off += 4;
}
}
off = 0x440;
for (i = 0; i < 12; i++)
writel(pd->timing[i], ctl_base + off + (4 * i));
if (1 == pinfo->num_of_lanes)
writel(0x8, ctl_base + 0x200 + (4 * 11));
if (pinfo->lane_swap)
writel(pinfo->lane_swap, ctl_base + 0x0ac);
/* T_CLK_POST, T_CLK_PRE for CLK lane P/N HS 200 mV timing
length should > data lane HS timing length */
writel(0x41b, ctl_base + 0x0c0);
return 0;
}
int mdss_dsi_phy_init(struct mipi_dsi_panel_config *pinfo, uint32_t ctl_base)
{
struct mdss_dsi_phy_ctrl *pd;
uint32_t i, off = 0, ln, offset;
if (mdp_get_revision() == MDP_REV_304)
return mdss_dsi_v2_phy_init(pinfo, ctl_base);
pd = (pinfo->mdss_dsi_phy_config);
/* Strength ctrl 0 */
writel(pd->strength[0], ctl_base + 0x0484);
mdss_dsi_phy_regulator_init(pd);
/* Strength ctrl 0 */
writel(0x00, ctl_base + 0x04dc);
off = 0x0440; /* phy timing ctrl 0 - 11 */
for (i = 0; i < 12; i++) {
writel(pd->timing[i], ctl_base + off);
dmb();
off += 4;
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_1 */
writel(0x00, ctl_base + 0x0474);
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
writel(0x5f, ctl_base + 0x0470);
dmb();
/* 4 lanes + clk lane configuration */
/* lane config n * (0 - 4) & DataPath setup */
for (ln = 0; ln < 5; ln++) {
off = 0x0300 + (ln * 0x40);
for (i = 0; i < 9; i++) {
offset = i + (ln * 9);
writel(pd->laneCfg[offset], ctl_base + off);
dmb();
off += 4;
}
}
/* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */
writel(0x5f, ctl_base + 0x0470);
/* DSI_PHY_DSIPHY_GLBL_TEST_CTRL */
if (ctl_base == MIPI_DSI0_BASE)
writel(0x01, ctl_base + 0x04d4);
else
writel(0x00, ctl_base + 0x04d4);
dmb();
off = 0x04b4; /* phy BIST ctrl 0 - 5 */
for (i = 0; i < 6; i++) {
writel(pd->bistCtrl[i], ctl_base + off);
off += 4;
}
dmb();
/* DSI_0_CLKOUT_TIMING_CTRL */
writel(0x41b, ctl_base + 0x0c4);
dmb();
}
void mdss_dsi_phy_contention_detection(
struct mipi_dsi_panel_config *pinfo,
uint32_t ctrl_base)
{
struct mdss_dsi_phy_ctrl *pd;
if (mdp_get_revision() == MDP_REV_304)
return;
pd = (pinfo->mdss_dsi_phy_config);
writel(pd->strength[1], ctrl_base + 0x0488);
dmb();
}