830 lines
22 KiB
C
830 lines
22 KiB
C
/* Copyright (c) 2008-2012, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
#include <linux/clk.h>
|
|
#include <mach/clk.h>
|
|
#include "msm_fb.h"
|
|
#include "mdp.h"
|
|
#include "mdp4.h"
|
|
#include "mipi_dsi.h"
|
|
#include "hdmi_msm.h"
|
|
#include <mach/msm_iomap.h>
|
|
|
|
/* HDMI PHY macros */
|
|
#define HDMI_PHY_REG_0 (0x00000400)
|
|
#define HDMI_PHY_REG_1 (0x00000404)
|
|
#define HDMI_PHY_REG_2 (0x00000408)
|
|
#define HDMI_PHY_REG_3 (0x0000040c)
|
|
#define HDMI_PHY_REG_4 (0x00000410)
|
|
#define HDMI_PHY_REG_5 (0x00000414)
|
|
#define HDMI_PHY_REG_6 (0x00000418)
|
|
#define HDMI_PHY_REG_7 (0x0000041c)
|
|
#define HDMI_PHY_REG_8 (0x00000420)
|
|
#define HDMI_PHY_REG_9 (0x00000424)
|
|
#define HDMI_PHY_REG_10 (0x00000428)
|
|
#define HDMI_PHY_REG_11 (0x0000042c)
|
|
#define HDMI_PHY_REG_12 (0x00000430)
|
|
#define HDMI_PHY_REG_BIST_CFG (0x00000434)
|
|
#define HDMI_PHY_DEBUG_BUS_SEL (0x00000438)
|
|
#define HDMI_PHY_REG_MISC0 (0x0000043c)
|
|
#define HDMI_PHY_REG_13 (0x00000440)
|
|
#define HDMI_PHY_REG_14 (0x00000444)
|
|
#define HDMI_PHY_REG_15 (0x00000448)
|
|
#define HDMI_PHY_CTRL (0x000002D4)
|
|
|
|
/* HDMI PHY/PLL bit field macros */
|
|
#define HDMI_PHY_PLL_STATUS0 (0x00000598)
|
|
#define SW_RESET BIT(2)
|
|
#define SW_RESET_PLL BIT(0)
|
|
#define PWRDN_B BIT(7)
|
|
|
|
/* multimedia sub system clock control */
|
|
char *mmss_cc_base = MSM_MMSS_CLK_CTL_BASE;
|
|
/* multimedia sub system sfpb */
|
|
char *mmss_sfpb_base;
|
|
void __iomem *periph_base;
|
|
|
|
static struct dsi_clk_desc dsicore_clk;
|
|
static struct dsi_clk_desc dsi_pclk;
|
|
|
|
static struct clk *dsi_byte_div_clk;
|
|
static struct clk *dsi_esc_clk;
|
|
static struct clk *dsi_m_pclk;
|
|
static struct clk *dsi_s_pclk;
|
|
|
|
static struct clk *amp_pclk;
|
|
int mipi_dsi_clk_on;
|
|
|
|
int mipi_dsi_clk_init(struct platform_device *pdev)
|
|
{
|
|
struct msm_fb_data_type *mfd;
|
|
struct device *dev = &pdev->dev;
|
|
|
|
mfd = platform_get_drvdata(pdev);
|
|
|
|
amp_pclk = clk_get(dev, "arb_clk");
|
|
if (IS_ERR_OR_NULL(amp_pclk)) {
|
|
pr_err("can't find amp_pclk\n");
|
|
amp_pclk = NULL;
|
|
goto mipi_dsi_clk_err;
|
|
}
|
|
|
|
dsi_m_pclk = clk_get(dev, "master_iface_clk");
|
|
if (IS_ERR_OR_NULL(dsi_m_pclk)) {
|
|
pr_err("can't find dsi_m_pclk\n");
|
|
dsi_m_pclk = NULL;
|
|
goto mipi_dsi_clk_err;
|
|
}
|
|
|
|
dsi_s_pclk = clk_get(dev, "slave_iface_clk");
|
|
if (IS_ERR_OR_NULL(dsi_s_pclk)) {
|
|
pr_err("can't find dsi_s_pclk\n");
|
|
dsi_s_pclk = NULL;
|
|
goto mipi_dsi_clk_err;
|
|
}
|
|
|
|
dsi_byte_div_clk = clk_get(dev, "byte_clk");
|
|
if (IS_ERR(dsi_byte_div_clk)) {
|
|
pr_err("can't find dsi_byte_div_clk\n");
|
|
dsi_byte_div_clk = NULL;
|
|
goto mipi_dsi_clk_err;
|
|
}
|
|
|
|
dsi_esc_clk = clk_get(dev, "esc_clk");
|
|
if (IS_ERR(dsi_esc_clk)) {
|
|
printk(KERN_ERR "can't find dsi_esc_clk\n");
|
|
dsi_esc_clk = NULL;
|
|
goto mipi_dsi_clk_err;
|
|
}
|
|
|
|
return 0;
|
|
|
|
mipi_dsi_clk_err:
|
|
mipi_dsi_clk_deinit(dev);
|
|
return -EPERM;
|
|
}
|
|
|
|
void mipi_dsi_clk_deinit(struct device *dev)
|
|
{
|
|
if (amp_pclk)
|
|
clk_put(amp_pclk);
|
|
if (amp_pclk)
|
|
clk_put(dsi_m_pclk);
|
|
if (dsi_s_pclk)
|
|
clk_put(dsi_s_pclk);
|
|
if (dsi_byte_div_clk)
|
|
clk_put(dsi_byte_div_clk);
|
|
if (dsi_esc_clk)
|
|
clk_put(dsi_esc_clk);
|
|
}
|
|
|
|
static void mipi_dsi_clk_ctrl(struct dsi_clk_desc *clk, int clk_en)
|
|
{
|
|
char *cc, *ns, *md;
|
|
int pmxo_sel = 0;
|
|
char mnd_en = 1, root_en = 1;
|
|
uint32 data, val;
|
|
|
|
cc = mmss_cc_base + 0x004c;
|
|
md = mmss_cc_base + 0x0050;
|
|
ns = mmss_cc_base + 0x0054;
|
|
|
|
if (clk_en) {
|
|
if (clk->mnd_mode == 0) {
|
|
data = clk->pre_div_func << 14;
|
|
data |= clk->src;
|
|
MIPI_OUTP_SECURE(ns, data);
|
|
MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
|
|
| (clk->mnd_mode << 6)
|
|
| (root_en << 2) | clk_en));
|
|
} else {
|
|
val = clk->d * 2;
|
|
data = (~val) & 0x0ff;
|
|
data |= clk->m << 8;
|
|
MIPI_OUTP_SECURE(md, data);
|
|
|
|
val = clk->n - clk->m;
|
|
data = (~val) & 0x0ff;
|
|
data <<= 24;
|
|
data |= clk->src;
|
|
MIPI_OUTP_SECURE(ns, data);
|
|
|
|
MIPI_OUTP_SECURE(cc, ((pmxo_sel << 8)
|
|
| (clk->mnd_mode << 6)
|
|
| (mnd_en << 5)
|
|
| (root_en << 2) | clk_en));
|
|
}
|
|
} else
|
|
MIPI_OUTP_SECURE(cc, 0);
|
|
|
|
wmb();
|
|
}
|
|
|
|
static void mipi_dsi_sfpb_cfg(void)
|
|
{
|
|
char *sfpb;
|
|
int data;
|
|
|
|
sfpb = mmss_sfpb_base + 0x058;
|
|
|
|
data = MIPI_INP(sfpb);
|
|
data |= 0x01800;
|
|
MIPI_OUTP(sfpb, data);
|
|
wmb();
|
|
}
|
|
|
|
static void mipi_dsi_pclk_ctrl(struct dsi_clk_desc *clk, int clk_en)
|
|
{
|
|
char *cc, *ns, *md;
|
|
char mnd_en = 1, root_en = 1;
|
|
uint32 data, val;
|
|
|
|
cc = mmss_cc_base + 0x0130;
|
|
md = mmss_cc_base + 0x0134;
|
|
ns = mmss_cc_base + 0x0138;
|
|
|
|
if (clk_en) {
|
|
if (clk->mnd_mode == 0) {
|
|
data = clk->pre_div_func << 12;
|
|
data |= clk->src;
|
|
MIPI_OUTP_SECURE(ns, data);
|
|
MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
|
|
| (root_en << 2) | clk_en));
|
|
} else {
|
|
val = clk->d * 2;
|
|
data = (~val) & 0x0ff;
|
|
data |= clk->m << 8;
|
|
MIPI_OUTP_SECURE(md, data);
|
|
|
|
val = clk->n - clk->m;
|
|
data = (~val) & 0x0ff;
|
|
data <<= 24;
|
|
data |= clk->src;
|
|
MIPI_OUTP_SECURE(ns, data);
|
|
|
|
MIPI_OUTP_SECURE(cc, ((clk->mnd_mode << 6)
|
|
| (mnd_en << 5)
|
|
| (root_en << 2) | clk_en));
|
|
}
|
|
} else
|
|
MIPI_OUTP_SECURE(cc, 0);
|
|
|
|
wmb();
|
|
}
|
|
|
|
static void mipi_dsi_ahb_en(void)
|
|
{
|
|
char *ahb;
|
|
|
|
ahb = mmss_cc_base + 0x08;
|
|
|
|
pr_debug("%s: ahb=%x %x\n",
|
|
__func__, (int) ahb, MIPI_INP_SECURE(ahb));
|
|
}
|
|
|
|
void mipi_dsi_lane_cfg(void)
|
|
{
|
|
int i, ln_offset;
|
|
|
|
ln_offset = 0x300;
|
|
for (i = 0; i < 4; i++) {
|
|
/* DSI1_DSIPHY_LN_CFG0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset, 0x80);
|
|
/* DSI1_DSIPHY_LN_CFG1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x04, 0x45);
|
|
/* DSI1_DSIPHY_LN_CFG2 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x08, 0x0);
|
|
/* DSI1_DSIPHY_LN_TEST_DATAPATH */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x0c, 0x0);
|
|
/* DSI1_DSIPHY_LN_TEST_STR0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x14, 0x1);
|
|
/* DSI1_DSIPHY_LN_TEST_STR1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + ln_offset + 0x18, 0x66);
|
|
ln_offset += 0x40;
|
|
}
|
|
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0400, 0x40); /* DSI1_DSIPHY_LNCK_CFG0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0404, 0x67); /* DSI1_DSIPHY_LNCK_CFG1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0408, 0x0); /* DSI1_DSIPHY_LNCK_CFG2 */
|
|
/* DSI1_DSIPHY_LNCK_TEST_DATAPATH */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x040c, 0x0);
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0414, 0x1); /* DSI1_DSIPHY_LNCK_TEST_STR0 */
|
|
/* DSI1_DSIPHY_LNCK_TEST_STR1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0418, 0x88);
|
|
}
|
|
|
|
void mipi_dsi_bist_ctrl(void)
|
|
{
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0f); /* DSI1_DSIPHY_BIST_CTRL4 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0490, 0x03); /* DSI1_DSIPHY_BIST_CTRL1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x048c, 0x03); /* DSI1_DSIPHY_BIST_CTRL0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x049c, 0x0); /* DSI1_DSIPHY_BIST_CTRL4 */
|
|
}
|
|
|
|
static void mipi_dsi_calibration(void)
|
|
{
|
|
int i = 0;
|
|
uint32 term_cnt = 5000;
|
|
int cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
|
|
|
|
/* DSI1_DSIPHY_REGULATOR_CAL_PWR_CFG */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0518, 0x03);
|
|
|
|
/* DSI1_DSIPHY_CAL_SW_CFG2 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0534, 0x0);
|
|
/* DSI1_DSIPHY_CAL_HW_CFG1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x053c, 0x5a);
|
|
/* DSI1_DSIPHY_CAL_HW_CFG3 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0544, 0x10);
|
|
/* DSI1_DSIPHY_CAL_HW_CFG4 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0548, 0x01);
|
|
/* DSI1_DSIPHY_CAL_HW_CFG0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0538, 0x01);
|
|
|
|
/* DSI1_DSIPHY_CAL_HW_TRIGGER */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x01);
|
|
usleep_range(5000, 5000);
|
|
/* DSI1_DSIPHY_CAL_HW_TRIGGER */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0528, 0x00);
|
|
|
|
cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
|
|
while (cal_busy & 0x10) {
|
|
i++;
|
|
if (i > term_cnt) {
|
|
pr_err("DSI1 PHY REGULATOR NOT READY,"
|
|
"exceeded polling TIMEOUT!\n");
|
|
break;
|
|
}
|
|
cal_busy = MIPI_INP(MIPI_DSI_BASE + 0x550);
|
|
}
|
|
}
|
|
|
|
void mipi_dsi_phy_rdy_poll(void)
|
|
{
|
|
uint32 phy_pll_busy;
|
|
uint32 i = 0;
|
|
uint32 term_cnt = 0xFFFFFF;
|
|
|
|
phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280);
|
|
while (!(phy_pll_busy & 0x1)) {
|
|
i++;
|
|
if (i > term_cnt) {
|
|
pr_err("DSI1 PHY NOT READY, exceeded polling TIMEOUT!\n");
|
|
break;
|
|
}
|
|
phy_pll_busy = MIPI_INP(MIPI_DSI_BASE + 0x280);
|
|
}
|
|
}
|
|
|
|
#define PREF_DIV_RATIO 27
|
|
struct dsiphy_pll_divider_config pll_divider_config;
|
|
|
|
int mipi_dsi_phy_pll_config(u32 clk_rate)
|
|
{
|
|
struct dsiphy_pll_divider_config *dividers;
|
|
u32 fb_divider, tmp;
|
|
dividers = &pll_divider_config;
|
|
|
|
/* DSIPHY_PLL_CTRL_x: 1 2 3 8 9 10 */
|
|
/* masks 0xff 0x07 0x3f 0x0f 0xff 0xff */
|
|
|
|
/* DSIPHY_PLL_CTRL_1 */
|
|
fb_divider = ((dividers->fb_divider) / 2) - 1;
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x204, fb_divider & 0xff);
|
|
|
|
/* DSIPHY_PLL_CTRL_2 */
|
|
tmp = MIPI_INP(MIPI_DSI_BASE + 0x208);
|
|
tmp &= ~0x07;
|
|
tmp |= (fb_divider >> 8) & 0x07;
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x208, tmp);
|
|
|
|
/* DSIPHY_PLL_CTRL_3 */
|
|
tmp = MIPI_INP(MIPI_DSI_BASE + 0x20c);
|
|
tmp &= ~0x3f;
|
|
tmp |= (dividers->ref_divider_ratio - 1) & 0x3f;
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x20c, tmp);
|
|
|
|
/* DSIPHY_PLL_CTRL_8 */
|
|
tmp = MIPI_INP(MIPI_DSI_BASE + 0x220);
|
|
tmp &= ~0x0f;
|
|
tmp |= (dividers->bit_clk_divider - 1) & 0x0f;
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x220, tmp);
|
|
|
|
/* DSIPHY_PLL_CTRL_9 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x224, (dividers->byte_clk_divider - 1));
|
|
|
|
/* DSIPHY_PLL_CTRL_10 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x228, (dividers->dsi_clk_divider - 1));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mipi_dsi_clk_div_config(uint8 bpp, uint8 lanes,
|
|
uint32 *expected_dsi_pclk)
|
|
{
|
|
u32 fb_divider, rate, vco;
|
|
u32 div_ratio = 0;
|
|
struct dsi_clk_mnd_table const *mnd_entry = mnd_table;
|
|
if (pll_divider_config.clk_rate == 0)
|
|
pll_divider_config.clk_rate = 454000000;
|
|
|
|
rate = pll_divider_config.clk_rate / 1000000; /* In Mhz */
|
|
|
|
if (rate < 125) {
|
|
vco = rate * 8;
|
|
div_ratio = 8;
|
|
} else if (rate < 250) {
|
|
vco = rate * 4;
|
|
div_ratio = 4;
|
|
} else if (rate < 600) {
|
|
vco = rate * 2;
|
|
div_ratio = 2;
|
|
} else {
|
|
vco = rate * 1;
|
|
div_ratio = 1;
|
|
}
|
|
|
|
/* find the mnd settings from mnd_table entry */
|
|
for (; mnd_entry != mnd_table + ARRAY_SIZE(mnd_table); ++mnd_entry) {
|
|
if (((mnd_entry->lanes) == lanes) &&
|
|
((mnd_entry->bpp) == bpp))
|
|
break;
|
|
}
|
|
|
|
if (mnd_entry == mnd_table + ARRAY_SIZE(mnd_table)) {
|
|
pr_err("%s: requested Lanes, %u & BPP, %u, not supported\n",
|
|
__func__, lanes, bpp);
|
|
return -EINVAL;
|
|
}
|
|
fb_divider = ((vco * PREF_DIV_RATIO) / 27);
|
|
pll_divider_config.fb_divider = fb_divider;
|
|
pll_divider_config.ref_divider_ratio = PREF_DIV_RATIO;
|
|
pll_divider_config.bit_clk_divider = div_ratio;
|
|
pll_divider_config.byte_clk_divider =
|
|
pll_divider_config.bit_clk_divider * 8;
|
|
pll_divider_config.dsi_clk_divider =
|
|
(mnd_entry->dsiclk_div) * div_ratio;
|
|
|
|
if (mnd_entry->dsiclk_d == 0) {
|
|
dsicore_clk.mnd_mode = 0;
|
|
dsicore_clk.src = 0x3;
|
|
dsicore_clk.pre_div_func = (mnd_entry->dsiclk_n - 1);
|
|
} else {
|
|
dsicore_clk.mnd_mode = 2;
|
|
dsicore_clk.src = 0x3;
|
|
dsicore_clk.m = mnd_entry->dsiclk_m;
|
|
dsicore_clk.n = mnd_entry->dsiclk_n;
|
|
dsicore_clk.d = mnd_entry->dsiclk_d;
|
|
}
|
|
|
|
if ((mnd_entry->pclk_d == 0)
|
|
|| (mnd_entry->pclk_m == 1)) {
|
|
dsi_pclk.mnd_mode = 0;
|
|
dsi_pclk.src = 0x3;
|
|
dsi_pclk.pre_div_func = (mnd_entry->pclk_n - 1);
|
|
*expected_dsi_pclk = ((vco * 1000000) /
|
|
((pll_divider_config.dsi_clk_divider)
|
|
* (mnd_entry->pclk_n)));
|
|
} else {
|
|
dsi_pclk.mnd_mode = 2;
|
|
dsi_pclk.src = 0x3;
|
|
dsi_pclk.m = mnd_entry->pclk_m;
|
|
dsi_pclk.n = mnd_entry->pclk_n;
|
|
dsi_pclk.d = mnd_entry->pclk_d;
|
|
*expected_dsi_pclk = ((vco * 1000000 * dsi_pclk.m) /
|
|
((pll_divider_config.dsi_clk_divider)
|
|
* (mnd_entry->pclk_n)));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void mipi_dsi_configure_serdes(void)
|
|
{
|
|
void __iomem *cc;
|
|
|
|
/* PHY registers programemd thru S2P interface */
|
|
if (periph_base) {
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000000b6);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000001b5);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000001b4);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000003b3);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000003a2);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000002a1);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000008a0);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00000d9f);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000109e);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000209d);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000109c);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000079a);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00000c99);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00002298);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000000a7);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000000a6);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000000a5);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00007fa4);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000eea8);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000006aa);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00002095);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00000493);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00001092);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00000691);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00005490);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000038d);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000148c);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000058b);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000078a);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00001f89);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00003388);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00006387);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00004886);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00005085);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00000084);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000da83);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x0000b182);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00002f81);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00004080);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x00004180);
|
|
MIPI_OUTP(periph_base + 0x2c, 0x000006aa);
|
|
}
|
|
|
|
cc = MIPI_DSI_BASE + 0x0130;
|
|
MIPI_OUTP(cc, 0x806c11c8);
|
|
MIPI_OUTP(cc, 0x804c11c8);
|
|
MIPI_OUTP(cc, 0x806d0080);
|
|
MIPI_OUTP(cc, 0x804d0080);
|
|
MIPI_OUTP(cc, 0x00000000);
|
|
MIPI_OUTP(cc, 0x807b1597);
|
|
MIPI_OUTP(cc, 0x805b1597);
|
|
MIPI_OUTP(cc, 0x807c0080);
|
|
MIPI_OUTP(cc, 0x805c0080);
|
|
MIPI_OUTP(cc, 0x00000000);
|
|
MIPI_OUTP(cc, 0x807911c8);
|
|
MIPI_OUTP(cc, 0x805911c8);
|
|
MIPI_OUTP(cc, 0x807a0080);
|
|
MIPI_OUTP(cc, 0x805a0080);
|
|
MIPI_OUTP(cc, 0x00000000);
|
|
MIPI_OUTP(cc, 0x80721555);
|
|
MIPI_OUTP(cc, 0x80521555);
|
|
MIPI_OUTP(cc, 0x80730000);
|
|
MIPI_OUTP(cc, 0x80530000);
|
|
MIPI_OUTP(cc, 0x00000000);
|
|
}
|
|
|
|
void mipi_dsi_phy_init(int panel_ndx, struct msm_panel_info const *panel_info,
|
|
int target_type)
|
|
{
|
|
struct mipi_dsi_phy_ctrl *pd;
|
|
int i, off;
|
|
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0001);/* start phy sw reset */
|
|
wmb();
|
|
usleep(1);
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x128, 0x0000);/* end phy w reset */
|
|
wmb();
|
|
usleep(1);
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x500, 0x0003);/* regulator_ctrl_0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x504, 0x0001);/* regulator_ctrl_1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x508, 0x0001);/* regulator_ctrl_2 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x50c, 0x0000);/* regulator_ctrl_3 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x510, 0x0100);/* regulator_ctrl_4 */
|
|
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x4b0, 0x04);/* DSIPHY_LDO_CNTRL */
|
|
|
|
pd = (panel_info->mipi).dsi_phy_db;
|
|
|
|
off = 0x0480; /* strength 0 - 2 */
|
|
for (i = 0; i < 3; i++) {
|
|
MIPI_OUTP(MIPI_DSI_BASE + off, pd->strength[i]);
|
|
wmb();
|
|
off += 4;
|
|
}
|
|
|
|
off = 0x0470; /* ctrl 0 - 3 */
|
|
for (i = 0; i < 4; i++) {
|
|
MIPI_OUTP(MIPI_DSI_BASE + off, pd->ctrl[i]);
|
|
wmb();
|
|
off += 4;
|
|
}
|
|
|
|
off = 0x0500; /* regulator ctrl 0 - 4 */
|
|
for (i = 0; i < 5; i++) {
|
|
MIPI_OUTP(MIPI_DSI_BASE + off, pd->regulator[i]);
|
|
wmb();
|
|
off += 4;
|
|
}
|
|
mipi_dsi_calibration();
|
|
mipi_dsi_lane_cfg(); /* lane cfgs */
|
|
mipi_dsi_bist_ctrl(); /* bist ctrl */
|
|
|
|
off = 0x0204; /* pll ctrl 1 - 19, skip 0 */
|
|
for (i = 1; i < 20; i++) {
|
|
MIPI_OUTP(MIPI_DSI_BASE + off, pd->pll[i]);
|
|
wmb();
|
|
off += 4;
|
|
}
|
|
|
|
if (panel_info)
|
|
mipi_dsi_phy_pll_config(panel_info->clk_rate);
|
|
|
|
/* pll ctrl 0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x200, pd->pll[0]);
|
|
wmb();
|
|
|
|
off = 0x0440; /* phy timing ctrl 0 - 11 */
|
|
for (i = 0; i < 12; i++) {
|
|
MIPI_OUTP(MIPI_DSI_BASE + off, pd->timing[i]);
|
|
wmb();
|
|
off += 4;
|
|
}
|
|
|
|
if (target_type == 1)
|
|
mipi_dsi_configure_serdes();
|
|
}
|
|
|
|
void cont_splash_clk_ctrl(int enable)
|
|
{
|
|
static int cont_splash_clks_enabled;
|
|
if (enable && !cont_splash_clks_enabled) {
|
|
clk_prepare_enable(dsi_byte_div_clk);
|
|
clk_prepare_enable(dsi_esc_clk);
|
|
cont_splash_clks_enabled = 1;
|
|
} else if (!enable && cont_splash_clks_enabled) {
|
|
clk_disable_unprepare(dsi_byte_div_clk);
|
|
clk_disable_unprepare(dsi_esc_clk);
|
|
cont_splash_clks_enabled = 0;
|
|
}
|
|
}
|
|
|
|
void mipi_dsi_prepare_clocks(void)
|
|
{
|
|
clk_prepare(amp_pclk);
|
|
clk_prepare(dsi_m_pclk);
|
|
clk_prepare(dsi_s_pclk);
|
|
clk_prepare(dsi_byte_div_clk);
|
|
clk_prepare(dsi_esc_clk);
|
|
}
|
|
|
|
void mipi_dsi_unprepare_clocks(void)
|
|
{
|
|
clk_unprepare(dsi_esc_clk);
|
|
clk_unprepare(dsi_byte_div_clk);
|
|
clk_unprepare(dsi_m_pclk);
|
|
clk_unprepare(dsi_s_pclk);
|
|
clk_unprepare(amp_pclk);
|
|
}
|
|
|
|
void mipi_dsi_ahb_ctrl(u32 enable)
|
|
{
|
|
static int ahb_ctrl_done;
|
|
if (enable) {
|
|
if (ahb_ctrl_done) {
|
|
pr_info("%s: ahb clks already ON\n", __func__);
|
|
return;
|
|
}
|
|
clk_enable(amp_pclk); /* clock for AHB-master to AXI */
|
|
clk_enable(dsi_m_pclk);
|
|
clk_enable(dsi_s_pclk);
|
|
mipi_dsi_ahb_en();
|
|
mipi_dsi_sfpb_cfg();
|
|
ahb_ctrl_done = 1;
|
|
} else {
|
|
if (ahb_ctrl_done == 0) {
|
|
pr_info("%s: ahb clks already OFF\n", __func__);
|
|
return;
|
|
}
|
|
clk_disable(dsi_m_pclk);
|
|
clk_disable(dsi_s_pclk);
|
|
clk_disable(amp_pclk); /* clock for AHB-master to AXI */
|
|
ahb_ctrl_done = 0;
|
|
}
|
|
}
|
|
|
|
void mipi_dsi_clk_enable(void)
|
|
{
|
|
u32 pll_ctrl = MIPI_INP(MIPI_DSI_BASE + 0x0200);
|
|
if (mipi_dsi_clk_on) {
|
|
pr_info("%s: mipi_dsi_clks already ON\n", __func__);
|
|
return;
|
|
}
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0200, pll_ctrl | 0x01);
|
|
mipi_dsi_phy_rdy_poll();
|
|
|
|
if (clk_set_rate(dsi_byte_div_clk, 1) < 0) /* divided by 1 */
|
|
pr_err("%s: dsi_byte_div_clk - "
|
|
"clk_set_rate failed\n", __func__);
|
|
if (clk_set_rate(dsi_esc_clk, esc_byte_ratio) < 0) /* divided by esc */
|
|
pr_err("%s: dsi_esc_clk - " /* clk ratio */
|
|
"clk_set_rate failed\n", __func__);
|
|
mipi_dsi_pclk_ctrl(&dsi_pclk, 1);
|
|
mipi_dsi_clk_ctrl(&dsicore_clk, 1);
|
|
clk_enable(dsi_byte_div_clk);
|
|
clk_enable(dsi_esc_clk);
|
|
mipi_dsi_clk_on = 1;
|
|
mdp4_stat.dsi_clk_on++;
|
|
}
|
|
|
|
void mipi_dsi_clk_disable(void)
|
|
{
|
|
if (mipi_dsi_clk_on == 0) {
|
|
pr_info("%s: mipi_dsi_clks already OFF\n", __func__);
|
|
return;
|
|
}
|
|
clk_disable(dsi_esc_clk);
|
|
clk_disable(dsi_byte_div_clk);
|
|
mipi_dsi_pclk_ctrl(&dsi_pclk, 0);
|
|
mipi_dsi_clk_ctrl(&dsicore_clk, 0);
|
|
/* DSIPHY_PLL_CTRL_0, disable dsi pll */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0200, 0x0);
|
|
mipi_dsi_clk_on = 0;
|
|
mdp4_stat.dsi_clk_off++;
|
|
}
|
|
|
|
void mipi_dsi_phy_ctrl(int on)
|
|
{
|
|
if (on) {
|
|
/* DSIPHY_PLL_CTRL_5 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x050);
|
|
} else {
|
|
/* DSIPHY_PLL_CTRL_5 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0214, 0x05f);
|
|
|
|
/* DSIPHY_REGULATOR_CTRL_0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0500, 0x02);
|
|
|
|
/* DSIPHY_CTRL_0 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0470, 0x00);
|
|
|
|
/* DSIPHY_CTRL_1 */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0474, 0x7f);
|
|
|
|
/* disable dsi clk */
|
|
MIPI_OUTP(MIPI_DSI_BASE + 0x0118, 0);
|
|
}
|
|
}
|
|
|
|
#ifdef CONFIG_FB_MSM_HDMI_COMMON
|
|
void hdmi_phy_reset(void)
|
|
{
|
|
unsigned int phy_reset_polarity = 0x0;
|
|
unsigned int pll_reset_polarity = 0x0;
|
|
|
|
unsigned int val = HDMI_INP_ND(HDMI_PHY_CTRL);
|
|
|
|
phy_reset_polarity = val >> 3 & 0x1;
|
|
pll_reset_polarity = val >> 1 & 0x1;
|
|
|
|
if (phy_reset_polarity == 0)
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET);
|
|
else
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET));
|
|
|
|
if (pll_reset_polarity == 0)
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL);
|
|
else
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL));
|
|
|
|
msleep(100);
|
|
|
|
if (phy_reset_polarity == 0)
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET));
|
|
else
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET);
|
|
|
|
if (pll_reset_polarity == 0)
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val & (~SW_RESET_PLL));
|
|
else
|
|
HDMI_OUTP(HDMI_PHY_CTRL, val | SW_RESET_PLL);
|
|
}
|
|
|
|
void hdmi_msm_reset_core(void)
|
|
{
|
|
hdmi_msm_clk(0);
|
|
udelay(5);
|
|
hdmi_msm_clk(1);
|
|
|
|
clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_ASSERT);
|
|
udelay(20);
|
|
clk_reset(hdmi_msm_state->hdmi_app_clk, CLK_RESET_DEASSERT);
|
|
}
|
|
|
|
void hdmi_msm_init_phy(int video_format)
|
|
{
|
|
uint32 offset;
|
|
pr_err("Video format is : %u\n", video_format);
|
|
|
|
HDMI_OUTP(HDMI_PHY_REG_0, 0x1B);
|
|
HDMI_OUTP(HDMI_PHY_REG_1, 0xf2);
|
|
|
|
offset = HDMI_PHY_REG_4;
|
|
while (offset <= HDMI_PHY_REG_11) {
|
|
HDMI_OUTP(offset, 0x0);
|
|
offset += 0x4;
|
|
}
|
|
|
|
HDMI_OUTP(HDMI_PHY_REG_3, 0x20);
|
|
}
|
|
|
|
void hdmi_msm_powerdown_phy(void)
|
|
{
|
|
/* Power down PHY */
|
|
HDMI_OUTP_ND(HDMI_PHY_REG_2, 0x7F); /*0b01111111*/
|
|
}
|
|
|
|
void hdmi_frame_ctrl_cfg(const struct hdmi_disp_mode_timing_type *timing)
|
|
{
|
|
/* 0x02C8 HDMI_FRAME_CTRL
|
|
* 31 INTERLACED_EN Interlaced or progressive enable bit
|
|
* 0: Frame in progressive
|
|
* 1: Frame is interlaced
|
|
* 29 HSYNC_HDMI_POL HSYNC polarity fed to HDMI core
|
|
* 0: Active Hi Hsync, detect the rising edge of hsync
|
|
* 1: Active lo Hsync, Detect the falling edge of Hsync
|
|
* 28 VSYNC_HDMI_POL VSYNC polarity fed to HDMI core
|
|
* 0: Active Hi Vsync, detect the rising edge of vsync
|
|
* 1: Active Lo Vsync, Detect the falling edge of Vsync
|
|
* 12 RGB_MUX_SEL ALPHA mdp4 input is RGB, mdp4 input is BGR
|
|
*/
|
|
HDMI_OUTP(0x02C8,
|
|
((timing->interlaced << 31) & 0x80000000)
|
|
| ((timing->active_low_h << 29) & 0x20000000)
|
|
| ((timing->active_low_v << 28) & 0x10000000));
|
|
}
|
|
|
|
void hdmi_msm_phy_status_poll(void)
|
|
{
|
|
unsigned int lock_det, phy_ready;
|
|
lock_det = 0x1 & HDMI_INP_ND(HDMI_PHY_PLL_STATUS0);
|
|
if (lock_det) {
|
|
pr_debug("HDMI Phy PLL Lock Detect Bit is set\n");
|
|
} else {
|
|
pr_debug("HDMI Phy Lock Detect Bit is not set,"
|
|
"waiting for lock detection\n");
|
|
do {
|
|
lock_det = 0x1 & \
|
|
HDMI_INP_ND(HDMI_PHY_PLL_STATUS0);
|
|
} while (!lock_det);
|
|
}
|
|
|
|
phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15);
|
|
if (phy_ready) {
|
|
pr_debug("HDMI Phy Status bit is set and ready\n");
|
|
} else {
|
|
pr_debug("HDMI Phy Status bit is not set,"
|
|
"waiting for ready status\n");
|
|
do {
|
|
phy_ready = 0x1 & HDMI_INP_ND(HDMI_PHY_REG_15);
|
|
} while (!phy_ready);
|
|
}
|
|
}
|
|
|
|
#endif
|