msm_shared: edp continuous splash support
Add edp-aux channel with isr polling mechanism to support
edp link training. Dual pipes (rgb1 and rgb2) of mdp are used
to fetch splash frame. Mdp configuration are retained and
propagate to android kernel as continuous splash enabled.
CRs-Fixed: 492419
Change-Id: I8d7b63c7e24b42152259ca586e68ab7c6f27b36a
diff --git a/platform/msm_shared/display.c b/platform/msm_shared/display.c
index 3bf9278..c420af6 100644
--- a/platform/msm_shared/display.c
+++ b/platform/msm_shared/display.c
@@ -258,6 +258,19 @@
if (ret)
goto msm_display_init_out;
+ /* pinfo prepare */
+ if (pdata->panel_info.prepare) {
+ /* this is for edp which pinfo derived from edid */
+ ret = pdata->panel_info.prepare();
+ panel->fb.width = panel->panel_info.xres;
+ panel->fb.height = panel->panel_info.yres;
+ panel->fb.stride = panel->panel_info.xres;
+ panel->fb.bpp = panel->panel_info.bpp;
+ }
+
+ if (ret)
+ goto msm_display_init_out;
+
ret = msm_fb_alloc(&(panel->fb));
if (ret)
goto msm_display_init_out;
diff --git a/platform/msm_shared/edp.c b/platform/msm_shared/edp.c
index d170f19..d7c3067 100644
--- a/platform/msm_shared/edp.c
+++ b/platform/msm_shared/edp.c
@@ -33,6 +33,8 @@
#define RGB_COMPONENTS 3
#define MAX_NUMBER_EDP_LANES 4
+static struct msm_panel_info *edp_pinfo;
+
static void edp_config_sync(void)
{
int ret = 0;
@@ -59,105 +61,150 @@
edp_write(EDP_BASE + 0xc, 0x57); /* EDP_CONFIGURATION_CTRL */
}
-static void edp_config_timing(void)
+static void edp_config_tu(void)
{
- edp_write(EDP_BASE + 0x98, 0);
- edp_write(EDP_BASE + 0x9C, 0x8200020); /* EDP_HSYNC_CTL */
- edp_write(EDP_BASE + 0x100, 0x233AC0); /* EDP_VSYNC_PERIOD_F0 */
- edp_write(EDP_BASE + 0x104, 0x0);
- edp_write(EDP_BASE + 0x10C, 0x0);
- edp_write(EDP_BASE + 0x130, 0x7ef0070); /* EDP_DISPLAY_HCTL */
- edp_write(EDP_BASE + 0x134, 0x0); /* EDP_ACTIVE_HCTL */
- edp_write(EDP_BASE + 0x110, 0xB330); /* EDP_DISPLAY_V_START_F0 */
- edp_write(EDP_BASE + 0x118, 0x22f98f); /* EDP_DISPLAY_V_END_F0 */
- edp_write(EDP_BASE + 0x114, 0x0);
- edp_write(EDP_BASE + 0x11C, 0x0);
- edp_write(EDP_BASE + 0x120, 0x0); /* EDP_ACTIVE_V_START_F0 */
- edp_write(EDP_BASE + 0x128, 0x0); /* EDP_ACTIVE_V_END_F0 */
- edp_write(EDP_BASE + 0x124, 0x0);
- edp_write(EDP_BASE + 0x12C, 0x0);
+ /* temporary */
+ edp_write(EDP_BASE + 0x160, 0x2b);
+ edp_write(EDP_BASE + 0x15c, 0x00320033);
+ edp_write(EDP_BASE + 0x34, 0x0023001a);
+
+}
+
+static void edp_config_timing(struct msm_panel_info *pinfo)
+{
+ unsigned long total_ver, total_hor;
+ unsigned long data;
+
+ dprintf(INFO, "%s: width=%d hporch= %d %d %d\n", __func__,
+ pinfo->xres, pinfo->lcdc.h_back_porch,
+ pinfo->lcdc.h_front_porch, pinfo->lcdc.h_pulse_width);
+
+ dprintf(INFO, "%s: height=%d vporch= %d %d %d\n", __func__,
+ pinfo->yres, pinfo->lcdc.v_back_porch,
+ pinfo->lcdc.v_front_porch, pinfo->lcdc.v_pulse_width);
+
+ total_hor = pinfo->xres + pinfo->lcdc.h_back_porch +
+ pinfo->lcdc.h_front_porch + pinfo->lcdc.h_pulse_width;
+
+ total_ver = pinfo->yres + pinfo->lcdc.v_back_porch +
+ pinfo->lcdc.v_front_porch + pinfo->lcdc.v_pulse_width;
+
+ data = total_ver;
+ data <<= 16;
+ data |= total_hor;
+ edp_write(EDP_BASE + 0x1c, data); /* EDP_TOTAL_HOR_VER */
+
+ data = (pinfo->lcdc.v_back_porch + pinfo->lcdc.v_pulse_width);
+ data <<= 16;
+ data |= (pinfo->lcdc.h_back_porch + pinfo->lcdc.h_pulse_width);
+ edp_write(EDP_BASE + 0x20, data); /* EDP_START_HOR_VER_FROM_SYNC */
+
+ data = pinfo->lcdc.v_pulse_width;
+ data <<= 16;
+ data |= pinfo->lcdc.h_pulse_width;
+ edp_write(EDP_BASE + 0x24, data); /* EDP_HSYNC_VSYNC_WIDTH_POLARITY */
+
+ data = pinfo->yres;
+ data <<= 16;
+ data |= pinfo->xres;
+ edp_write(EDP_BASE + 0x28, data); /* EDP_ACTIVE_HOR_VER */
}
static void edp_enable(int enable)
{
edp_write(EDP_BASE + 0x8, 0x0); /* EDP_STATE_CTRL */
edp_write(EDP_BASE + 0x8, 0x40); /* EDP_STATE_CTRL */
- edp_write(EDP_BASE + 0x94, enable); /* EDP_TIMING_ENGINE_EN */
- edp_write(EDP_BASE + 0x4, enable); /* EDP_MAINLINK_CTRL */
+ edp_write(EDP_BASE + 0x4, 0x01); /* EDP_MAINLINK_CTRL */
}
-/*
- * Converts from EDID struct to msm_panel_info
- */
-void edp_edid2pinfo(struct edp_panel_data *edp_panel)
+static void edp_disable(int enable)
{
- struct display_timing_desc *dp;
- struct msm_panel_info *pinfo;
-
- dp = &edp_panel->edid.timing[0];
- pinfo = &edp_panel->panel_data->panel_info;
-
- pinfo->clk_rate = dp->pclk;
-
- pinfo->xres = dp->h_addressable + dp->h_border * 2;
- pinfo->yres = dp->v_addressable + dp->v_border * 2;
-
- pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
- - dp->h_sync_pulse;
- pinfo->lcdc.h_front_porch = dp->h_fporch;
- pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
-
- pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
- - dp->v_sync_pulse;
- pinfo->lcdc.v_front_porch = dp->v_fporch;
- pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
-
- pinfo->type = EDP_PANEL;
- pinfo->wait_cycle = 0;
- pinfo->bpp = 24;
-
- pinfo->lcdc.border_clr = 0; /* black */
- pinfo->lcdc.underflow_clr = 0xff; /* blue */
- pinfo->lcdc.hsync_skew = 0;
+ edp_write(EDP_BASE + 0x8, 0x0); /* EDP_STATE_CTRL */
+ edp_write(EDP_BASE + 0x4, 0x00); /* EDP_MAINLINK_CTRL */
}
int edp_on(void)
{
- int i;
-
- edp_phy_sw_reset();
- edp_pll_configure();
- edp_config_clk();
+ mdss_edp_pll_configure();
+ mdss_edp_phy_pll_ready();
edp_phy_misc_cfg();
edp_config_sync();
edp_config_sw_div();
edp_config_static_mdiv();
- edp_config_timing();
+ edp_config_timing(edp_pinfo);
+ edp_config_tu();
- edp_hw_powerup(1);
-
- for (i = 0; i < MAX_NUMBER_EDP_LANES; ++i)
- edp_enable_lane_bist(i, 1);
+ edp_config_clk();
+ mdss_edp_lane_power_ctrl(1);
edp_enable_mainlink(1);
+
+ mdss_edp_link_train();
+
edp_enable(1);
+ mdss_edp_wait_for_video_ready();
+
+ mdss_edp_irq_disable();
+ dprintf(INFO, "%s:\n", __func__);
+
return 0;
}
int edp_off(void)
{
- int i;
-
- mdp_edp_off();
- edp_enable(0);
- edp_unconfig_clk();
+ mdss_edp_irq_disable();
edp_enable_mainlink(0);
+ edp_phy_pll_reset();
+ edp_mainlink_reset();
+ edp_aux_reset();
- for (i = 0; i < MAX_NUMBER_EDP_LANES; ++i)
- edp_enable_lane_bist(i, 0);
+ edp_disable(1);
+ edp_unconfig_clk();
- edp_hw_powerup(0);
+ mdss_edp_lane_power_ctrl(0);
+ edp_phy_powerup(0);
+
+ dprintf(INFO, "%s:\n", __func__);
+
return 0;
}
+
+int edp_prepare(void)
+{
+
+ mdss_edp_aux_init();
+ edp_phy_pll_reset();
+ edp_mainlink_reset();
+ edp_aux_reset();
+ edp_phy_powerup(1);
+ edp_aux_enable();
+ mdss_edp_irq_enable();
+
+ mdss_edp_wait_for_hpd();
+
+ mdss_edp_edid_read();
+ mdss_edp_dpcd_cap_read();
+
+ edp_edid2pinfo(edp_pinfo);
+ edp_cap2pinfo(edp_pinfo);
+
+ dprintf(INFO, "%s:\n", __func__);
+
+ return 0;
+}
+
+void edp_panel_init(struct msm_panel_info *pinfo)
+{
+ if (!pinfo)
+ return;
+
+ pinfo->lcdc.dual_pipe = 1;
+ pinfo->lcdc.split_display = 0;
+
+ edp_pinfo = pinfo;
+ edp_pinfo->on = edp_on;
+ edp_pinfo->off = edp_off;
+ edp_pinfo->prepare = edp_prepare;
+}
diff --git a/platform/msm_shared/edp_aux.c b/platform/msm_shared/edp_aux.c
new file mode 100644
index 0000000..f7b5cfc
--- /dev/null
+++ b/platform/msm_shared/edp_aux.c
@@ -0,0 +1,1024 @@
+/* Copyright (c) 2013, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE 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 "edp.h"
+
+struct edp_aux_ctrl edpctrl;
+
+int edp_hpd_done = 0;
+int edp_video_ready = 0;
+
+/*
+ * edid
+ */
+static char edid_hdr[8] = {0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00};
+
+
+int edp_edid_buf_error(char *buf, int len)
+{
+ char *bp;
+ int i;
+ char csum = 0;
+ int ret = 0;
+
+ bp = buf;
+ if (len < 128) {
+ dprintf(INFO, "edp_edid_bur_error: Error: len=%x\n", len);
+ return -1;
+ }
+
+ for (i = 0; i < 128; i++)
+ csum += *bp++;
+
+ if (csum != 0) {
+ dprintf(INFO, "edp_edid_bur_error: Error: csum=%x\n", csum);
+ return -1;
+ }
+
+ if (buf[1] != 0xff) {
+ dprintf(INFO, "edp_edid_buf_error: Error: header\n");
+ return -1;
+ }
+
+ return ret;
+}
+
+
+void edp_extract_edid_manufacturer(struct edp_edid *edid, char *buf)
+{
+ char *bp;
+ char data;
+
+ bp = &buf[8];
+ data = *bp & 0x7f;
+ data >>= 2;
+ edid->id_name[0] = 'A' + data - 1;
+ data = *bp & 0x03;
+ data <<= 3;
+ bp++;
+ data |= (*bp >> 5);
+ edid->id_name[1] = 'A' + data - 1;
+ data = *bp & 0x1f;
+ edid->id_name[2] = 'A' + data - 1;
+ edid->id_name[3] = 0;
+
+ dprintf(INFO, "%s: edid manufacturer = %s", __func__,edid->id_name);
+}
+
+void edp_extract_edid_product(struct edp_edid *edid, char *buf)
+{
+ char *bp;
+ int data;
+
+ bp = &buf[0x0a];
+ data = *bp;
+ edid->id_product = *bp++;
+ edid->id_product &= 0x0ff;
+ data = *bp & 0x0ff;
+ data <<= 8;
+ edid->id_product |= data;
+
+ dprintf(INFO, "edid product = 0x%x", edid->id_product);
+};
+
+void edp_extract_edid_version(struct edp_edid *edid, char *buf)
+{
+ edid->version = buf[0x12];
+ edid->revision = buf[0x13];
+ dprintf(INFO, "edid version = %d.%d", edid->version,
+ edid->revision);
+};
+
+void edp_extract_edid_ext_block_cnt(struct edp_edid *edid, char *buf)
+{
+ edid->ext_block_cnt = buf[0x7e];
+ dprintf(INFO, "edid extension = %d", edid->ext_block_cnt);
+};
+
+void edp_extract_edid_video_support(struct edp_edid *edid, char *buf)
+{
+ char *bp;
+
+ bp = &buf[0x14];
+ if (*bp & 0x80) {
+ edid->video_intf = *bp & 0x0f;
+ /* 6, 8, 10, 12, 14 and 16 bit per component */
+ edid->color_depth = ((*bp & 0x70) >> 4); /* color bit depth */
+ if (edid->color_depth) {
+ edid->color_depth *= 2;
+ edid->color_depth += 4;
+ }
+ dprintf(INFO, "Digital Video intf=%d color_depth=%d\n",
+ edid->video_intf, edid->color_depth);
+ return;
+ }
+ dprintf(INFO, "Error, Analog video interface");
+};
+
+void edp_extract_edid_feature(struct edp_edid *edid, char *buf)
+{
+ char *bp;
+ char data;
+
+ bp = &buf[0x18];
+ data = *bp;
+ data &= 0xe0;
+ data >>= 5;
+ if (data == 0x01)
+ edid->dpm = 1; /* display power management */
+
+ if (edid->video_intf) {
+ if (*bp & 0x80) {
+ /* RGB 4:4:4, YcrCb 4:4:4 and YCrCb 4:2:2 */
+ edid->color_format = *bp & 0x18;
+ edid->color_format >>= 3;
+ }
+ }
+
+ dprintf(INFO, "edid dpm=%d color_format=%d",
+ edid->dpm, edid->color_format);
+};
+
+void edp_extract_edid_detailed_timing_description(struct edp_edid *edid,
+ char *buf)
+{
+ char *bp;
+ int data;
+ struct display_timing_desc *dp;
+
+ dp = &edid->timing[0];
+
+ bp = &buf[0x36];
+ dp->pclk = 0;
+ dp->pclk = *bp++; /* byte 0x36 */
+ dp->pclk |= (*bp++ << 8); /* byte 0x37 */
+
+ dp->h_addressable = *bp++; /* byte 0x38 */
+
+ if (dp->pclk == 0 && dp->h_addressable == 0)
+ return; /* Not detailed timing definition */
+
+ dp->pclk *= 10000;
+
+ dp->h_blank = *bp++;/* byte 0x39 */
+ data = *bp & 0xf0; /* byte 0x3A */
+ data <<= 4;
+ dp->h_addressable |= data;
+
+ data = *bp++ & 0x0f;
+ data <<= 8;
+ dp->h_blank |= data;
+
+ dp->v_addressable = *bp++; /* byte 0x3B */
+ dp->v_blank = *bp++; /* byte 0x3C */
+ data = *bp & 0xf0; /* byte 0x3D */
+ data <<= 4;
+ dp->v_addressable |= data;
+
+ data = *bp++ & 0x0f;
+ data <<= 8;
+ dp->v_blank |= data;
+
+ dp->h_fporch = *bp++; /* byte 0x3E */
+ dp->h_sync_pulse = *bp++; /* byte 0x3F */
+
+ dp->v_fporch = *bp & 0x0f0; /* byte 0x40 */
+ dp->v_fporch >>= 4;
+ dp->v_sync_pulse = *bp & 0x0f;
+
+ bp++;
+ data = *bp & 0xc0; /* byte 0x41 */
+ data <<= 2;
+ dp->h_fporch |= data;
+
+ data = *bp & 0x30;
+ data <<= 4;
+ dp->h_sync_pulse |= data;
+
+ data = *bp & 0x0c;
+ data <<= 2;
+ dp->v_fporch |= data;
+
+ data = *bp & 0x03;
+ data <<= 4;
+ dp->v_sync_pulse |= data;
+
+ bp++;
+ dp->width_mm = *bp++; /* byte 0x42 */
+ dp->height_mm = *bp++; /* byte 0x43 */
+ data = *bp & 0x0f0; /* byte 0x44 */
+ data <<= 4;
+ dp->width_mm |= data;
+ data = *bp & 0x0f;
+ data <<= 8;
+ dp->height_mm |= data;
+
+ bp++;
+ dp->h_border = *bp++; /* byte 0x45 */
+ dp->v_border = *bp++; /* byte 0x46 */
+
+ dp->interlaced = *bp & 0x80; /* byte 0x47 */
+
+ dp->stereo = *bp & 0x60;
+ dp->stereo >>= 5;
+
+ data = *bp & 0x1e; /* bit 4,3,2 1*/
+ data >>= 1;
+ dp->sync_type = data & 0x08;
+ dp->sync_type >>= 3; /* analog or digital */
+ if (dp->sync_type) {
+ dp->sync_separate = data & 0x04;
+ dp->sync_separate >>= 2;
+ if (dp->sync_separate) {
+ if (data & 0x02)
+ dp->vsync_pol = 1; /* positive */
+ else
+ dp->vsync_pol = 0;/* negative */
+
+ if (data & 0x01)
+ dp->hsync_pol = 1; /* positive */
+ else
+ dp->hsync_pol = 0; /* negative */
+ }
+ }
+
+ dprintf(INFO, "pixel_clock = %d\n", dp->pclk);
+
+ dprintf(INFO, "horizontal=%d, blank=%d, porch=%d, sync=%d\n"
+ , dp->h_addressable, dp->h_blank,
+ dp->h_fporch, dp->h_sync_pulse);
+ dprintf(INFO, "vertical=%d, blank=%d, porch=%d, vsync=%d\n"
+ , dp->v_addressable, dp->v_blank,
+ dp->v_fporch, dp->v_sync_pulse);
+ dprintf(INFO, "panel size in mm, width=%d height=%d\n",
+ dp->width_mm, dp->height_mm);
+ dprintf(INFO, "panel border horizontal=%d vertical=%d\n",
+ dp->h_border, dp->v_border);
+ dprintf(INFO, "flags: interlaced=%d stereo=%d sync_type=%d sync_sep=%d\n"
+ , dp->interlaced, dp->stereo,
+ dp->sync_type, dp->sync_separate);
+ dprintf(INFO, "polarity vsync=%d, hsync=%d\n",
+ dp->vsync_pol, dp->hsync_pol);
+}
+
+
+/*
+ * EDID structure can be found in VESA standart here:
+ * http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf
+ *
+ * following table contains default edid
+ * static char edid_raw_data[128] = {
+ * 0, 255, 255, 255, 255, 255, 255, 0,
+ * 6, 175, 93, 48, 0, 0, 0, 0, 0, 22,
+ * 1, 4,
+ * 149, 26, 14, 120, 2,
+ * 164, 21,158, 85, 78, 155, 38, 15, 80, 84,
+ * 0, 0, 0,
+ * 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ * 29, 54, 128, 160, 112, 56, 30, 64, 48, 32, 142, 0, 0, 144, 16,0,0,24,
+ * 19, 36, 128, 160, 112, 56, 30, 64, 48, 32, 142, 0, 0, 144, 16,0,0,24,
+ * 0, 0, 0, 254, 0, 65, 85, 79, 10, 32, 32, 32, 32, 32, 32, 32, 32, 32,
+ * 0, 0, 0, 254, 0, 66, 49, 49, 54, 72, 65, 78, 48, 51, 46, 48, 32, 10,
+ * 0, 75 };
+ */
+
+static int edp_aux_chan_ready(struct edp_aux_ctrl *ep)
+{
+ int cnt, ret;
+ char data = 0;
+
+ cnt = 5;
+ while(cnt--) {
+ ret = edp_aux_write_buf(ep, 0x50, &data, 1, 1);
+ dprintf(INFO, "edp_aux_chan_ready: ret=%d\n", ret);
+ if (ret >= 0)
+ break;
+ dprintf(INFO, "edp_aux_chan_ready: failed in write\n");
+ mdelay(100);
+ }
+
+ if (cnt == 0)
+ return 0;
+
+ return 1;
+}
+
+static int edp_sink_edid_read(struct edp_aux_ctrl *ep, int block)
+{
+ struct edp_buf *rp;
+ int cnt, rlen;
+ char data = 0;
+ int ret = 0;
+
+start:
+ cnt = 5;
+dprintf(INFO, "%s: cnt=%d\n", __func__, cnt);
+ /* need to write a dummy byte before read edid */
+ while(cnt--) {
+ ret = edp_aux_write_buf(ep, 0x50, &data, 1, 1);
+ if (ret >= 0)
+ break;
+ dprintf(INFO, "edp_sink_edid_read: failed in write\n");
+ mdelay(100);
+ }
+
+ if (cnt == 0)
+ return -1;
+
+ rlen = edp_aux_read_buf(ep, 0x50, 128, 1);
+
+dprintf(INFO, "edp_sink_edid_read: rlen=%d\n", rlen);
+
+ if (rlen < 0)
+ goto start;
+
+ rp = &ep->rxp;
+ if (edp_edid_buf_error(rp->data, rp->len))
+ goto start;
+
+ edp_extract_edid_manufacturer(&ep->edid, rp->data);
+ edp_extract_edid_product(&ep->edid, rp->data);
+ edp_extract_edid_version(&ep->edid, rp->data);
+ edp_extract_edid_ext_block_cnt(&ep->edid, rp->data);
+ edp_extract_edid_video_support(&ep->edid, rp->data);
+ edp_extract_edid_feature(&ep->edid, rp->data);
+ edp_extract_edid_detailed_timing_description(&ep->edid, rp->data);
+
+ return 128;
+}
+
+/*
+ * Converts from EDID struct to msm_panel_info
+ */
+void edp_edid2pinfo(struct msm_panel_info *pinfo)
+{
+ struct display_timing_desc *dp;
+
+ dp = &edpctrl.edid.timing[0];
+
+ pinfo->clk_rate = dp->pclk;
+
+ dprintf(SPEW, "%s: pclk=%d\n", __func__, pinfo->clk_rate);
+
+ pinfo->xres = dp->h_addressable + dp->h_border * 2;
+ pinfo->yres = dp->v_addressable + dp->v_border * 2;
+
+ pinfo->lcdc.h_back_porch = dp->h_blank - dp->h_fporch \
+ - dp->h_sync_pulse;
+ pinfo->lcdc.h_front_porch = dp->h_fporch;
+ pinfo->lcdc.h_pulse_width = dp->h_sync_pulse;
+
+ pinfo->lcdc.v_back_porch = dp->v_blank - dp->v_fporch \
+ - dp->v_sync_pulse;
+ pinfo->lcdc.v_front_porch = dp->v_fporch;
+ pinfo->lcdc.v_pulse_width = dp->v_sync_pulse;
+
+ pinfo->type = EDP_PANEL;
+ pinfo->wait_cycle = 0;
+ pinfo->bpp = 24;
+
+ pinfo->lcdc.border_clr = 0; /* black */
+ pinfo->lcdc.underflow_clr = 0xff; /* blue */
+ pinfo->lcdc.hsync_skew = 0;
+}
+
+void edp_cap2pinfo(struct msm_panel_info *pinfo)
+{
+ struct dpcd_cap *cap;
+
+ cap = &edpctrl.dpcd;
+
+ pinfo->edp.max_lane_count = cap->max_lane_count;
+ pinfo->edp.max_link_clk = cap->max_link_rate;
+
+ dprintf(SPEW, "%s: clk=%d lane=%d\n", __func__,
+ pinfo->edp.max_lane_count, pinfo->edp.max_link_clk);
+}
+
+static void edp_sink_capability_read(struct edp_aux_ctrl *ep,
+ int len)
+{
+ char *bp;
+ char data;
+ struct dpcd_cap *cap;
+ struct edp_buf *rp;
+ int rlen;
+
+ dprintf(INFO, "%s:\n",__func__);
+
+ rlen = edp_aux_read_buf(ep, 0, len, 0);
+ if (rlen <= 0) {
+ dprintf(INFO, "edp_sink_capability_read: edp aux read failed\n");
+ return;
+ }
+ rp = &ep->rxp;
+ cap = &ep->dpcd;
+ bp = rp->data;
+
+ data = *bp++; /* byte 0 */
+ cap->major = (data >> 4) & 0x0f;
+ cap->minor = data & 0x0f;
+ if (--rlen <= 0)
+ return;
+ dprintf(INFO, "edp_sink_cap_read: version: %d.%d\n", cap->major, cap->minor);
+
+ data = *bp++; /* byte 1 */
+ /* 162, 270 and 540 MB, symbol rate, NOT bit rate */
+ cap->max_link_rate = data * 27;
+ if (--rlen <= 0)
+ return;
+ dprintf(INFO, "edp_sink_cap_read: link_rate=%d\n", cap->max_link_rate);
+
+ data = *bp++; /* byte 2 */
+ if (data & BIT(7))
+ cap->flags |= DPCD_ENHANCED_FRAME;
+ if (data & 0x40)
+ cap->flags |= DPCD_TPS3;
+ data &= 0x0f;
+ cap->max_lane_count = data;
+ if (--rlen <= 0)
+ return;
+ dprintf(INFO, "edp_sink_cap_read: lane_count=%d\n", cap->max_lane_count);
+
+ data = *bp++; /* byte 3 */
+ if (data & BIT(0)) {
+ cap->flags |= DPCD_MAX_DOWNSPREAD_0_5;
+ dprintf(INFO, "edp_sink_cap_read: max_downspread\n");
+ }
+
+ if (data & BIT(6)) {
+ cap->flags |= DPCD_NO_AUX_HANDSHAKE;
+ dprintf(INFO, "edp_sink_cap_read: NO Link Training\n");
+ }
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 4 */
+ cap->num_rx_port = (data & BIT(0)) + 1;
+ dprintf(INFO, "edp_sink_cap_read: rx_ports=%d", cap->num_rx_port);
+ if (--rlen <= 0)
+ return;
+
+ bp += 3; /* skip 5, 6 and 7 */
+ rlen -= 3;
+ if (rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 8 */
+ if (data & BIT(1)) {
+ cap->flags |= DPCD_PORT_0_EDID_PRESENTED;
+ dprintf(INFO, "edp_sink_cap_read: edid presented\n");
+ }
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 9 */
+ cap->rx_port0_buf_size = (data + 1) * 32;
+ dprintf(INFO, "edp_sink_cap_read: lane_buf_size=%d", cap->rx_port0_buf_size);
+ if (--rlen <= 0)
+ return;
+
+ bp += 2; /* skip 10, 11 port1 capability */
+ rlen -= 2;
+ if (rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 12 */
+ cap->i2c_speed_ctrl = data;
+ if (cap->i2c_speed_ctrl > 0)
+ dprintf(INFO, "edp_sink_cap_read: i2c_rate=%d", cap->i2c_speed_ctrl);
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 13 */
+ cap->scrambler_reset = data & BIT(0);
+ dprintf(INFO, "edp_sink_cap_read: scrambler_reset=%d\n",
+ cap->scrambler_reset);
+
+ cap->enhanced_frame = data & BIT(1);
+ dprintf(INFO, "edp_sink_cap_read: enhanced_framing=%d\n",
+ cap->enhanced_frame);
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 14 */
+ if (data == 0)
+ cap->training_read_interval = 100; /* us */
+ else
+ cap->training_read_interval = 4000 * data; /* us */
+ dprintf(INFO, "edp_sink_cap_read: training_interval=%d\n",
+ cap->training_read_interval);
+}
+
+static void edp_link_status_read(struct edp_aux_ctrl *ep, int len)
+{
+ char *bp;
+ char data;
+ struct dpcd_link_status *sp;
+ struct edp_buf *rp;
+ int rlen;
+
+
+ /* skip byte 0x200 and 0x201 */
+ rlen = edp_aux_read_buf(ep, 0x202, len, 0);
+ dprintf(INFO, "%s: rlen=%d\n", __func__, rlen);
+ if (rlen <= 0) {
+ dprintf(INFO, "edp_link_status_read: edp aux read failed\n");
+ return;
+ }
+ rp = &ep->rxp;
+ bp = rp->data;
+ sp = &ep->link_status;
+
+ data = *bp++; /* byte 0x202 */
+ sp->lane_01_status = data; /* lane 0, 1 */
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 0x203 */
+ sp->lane_23_status = data; /* lane 2, 3 */
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 0x204 */
+ sp->interlane_align_done = (data & BIT(0));
+ sp->downstream_port_status_changed = (data & BIT(6));
+ sp->link_status_updated = (data & BIT(7));
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 0x205 */
+ sp->port_0_in_sync = (data & BIT(0));
+ sp->port_1_in_sync = (data & BIT(1));
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 0x206 */
+ sp->req_voltage_swing[0] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[0] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[1] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[1] = data & 0x03;
+ if (--rlen <= 0)
+ return;
+
+ data = *bp++; /* byte 0x207 */
+ sp->req_voltage_swing[2] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[2] = data & 0x03;
+ data >>= 2;
+ sp->req_voltage_swing[3] = data & 0x03;
+ data >>= 2;
+ sp->req_pre_emphasis[3] = data & 0x03;
+
+ bp = rp->data;
+dprintf(INFO, "%s: %x %x %x %x %x %x\n", __func__, *bp,
+ *(bp+1), *(bp+2), *(bp+3), *(bp+4), *(bp+5));
+
+ dprintf(INFO, "%s: align=%d v=%d p=%d\n", __func__,
+ sp->interlane_align_done, sp->req_voltage_swing[0], sp->req_pre_emphasis[0]);
+}
+
+
+static int edp_cap_lane_rate_set(struct edp_aux_ctrl *ep)
+{
+ char buf[4];
+ int len = 0;
+
+ dprintf(INFO, "cap_lane_set: bw=%x lane=%d\n", ep->link_rate, ep->lane_cnt);
+ buf[0] = ep->link_rate;
+ buf[1] = ep->lane_cnt;
+ len = edp_aux_write_buf(ep, 0x100, buf, 2, 0);
+
+ return len;
+}
+
+static int edp_lane_set_write(struct edp_aux_ctrl *ep, int voltage_level,
+ int pre_emphasis_level)
+{
+ int i;
+ char buf[4];
+
+
+ if (voltage_level >= DPCD_LINK_VOLTAGE_MAX)
+ voltage_level |= 0x04;
+
+ if (pre_emphasis_level >= DPCD_LINK_PRE_EMPHASIS_MAX)
+ pre_emphasis_level |= 0x04;
+
+ pre_emphasis_level <<= 3;
+
+ for (i = 0; i < 4; i++)
+ buf[i] = voltage_level | pre_emphasis_level;
+
+ dprintf(INFO, "%s: p|v=0x%x\n", __func__, voltage_level | pre_emphasis_level);
+ return edp_aux_write_buf(ep, 0x103, buf, 4, 0);
+}
+
+static int edp_powerstate_write(struct edp_aux_ctrl *ep,
+ char powerstate)
+{
+ return edp_aux_write_buf(ep, 0x600, &powerstate, 1, 0);
+}
+
+static int edp_train_pattern_set_write(struct edp_aux_ctrl *ep,
+ int pattern)
+{
+ char buf[4];
+
+ buf[0] = pattern;
+ return edp_aux_write_buf(ep, 0x102, buf, 1, 0);
+}
+
+static int edp_sink_clock_recovery_done(struct edp_aux_ctrl *ep)
+{
+ int mask;
+ int data;
+
+
+ if (ep->lane_cnt == 1) {
+ mask = 0x01; /* lane 0 */
+ data = ep->link_status.lane_01_status;
+ } else if (ep->lane_cnt == 2) {
+ mask = 0x011; /*B lane 0, 1 */
+ data = ep->link_status.lane_01_status;
+ } else {
+ mask = 0x01111; /*B lane 0, 1 */
+ data = ep->link_status.lane_23_status;
+ data <<= 8;
+ data |= ep->link_status.lane_01_status;
+ }
+
+dprintf(INFO, "clock_recovery_done: data=%x mask=%x\n", data, mask);
+ data &= mask;
+ if (data == mask) /* all done */
+ return 1;
+
+ return 0;
+}
+
+static int edp_sink_channel_eq_done(struct edp_aux_ctrl *ep)
+{
+ int mask;
+ int data;
+
+
+ if (!ep->link_status.interlane_align_done) /* not align */
+ return 0;
+
+ if (ep->lane_cnt == 1) {
+ mask = 0x7;
+ data = ep->link_status.lane_01_status;
+ } else if (ep->lane_cnt == 2) {
+ mask = 0x77;
+ data = ep->link_status.lane_01_status;
+ } else {
+ mask = 0x7777;
+ data = ep->link_status.lane_23_status;
+ data <<= 8;
+ data |= ep->link_status.lane_01_status;
+ }
+
+dprintf(INFO, "%s: data=%x mask=%x\n", __func__, data, mask);
+
+ data &= mask;
+ if (data == mask)/* all done */
+ return 1;
+
+ return 0;
+}
+
+void edp_sink_train_set_adjust(struct edp_aux_ctrl *ep)
+{
+ int i;
+ int max = 0;
+
+
+ /* use the max level across lanes */
+ for (i = 0; i < ep->lane_cnt; i++) {
+ if (max < ep->link_status.req_voltage_swing[i])
+ max = ep->link_status.req_voltage_swing[i];
+ }
+
+ ep->v_level = max;
+
+ /* use the max level across lanes */
+ max = 0;
+ for (i = 0; i < ep->lane_cnt; i++) {
+ if (max < ep->link_status.req_pre_emphasis[i])
+ max = ep->link_status.req_pre_emphasis[i];
+ }
+
+ ep->p_level = max;
+ dprintf(INFO, "train_set_adjust: v_level=%d, p_level=%d\n",
+ ep->v_level, ep->p_level);
+}
+
+static void edp_host_train_set(struct edp_aux_ctrl *ep, int train)
+{
+ int bit, cnt;
+ int data;
+
+
+ bit = 1;
+ bit <<= (train - 1);
+ edp_write(EDP_BASE + EDP_STATE_CTRL, bit);
+
+ bit = 8;
+ bit <<= (train - 1);
+ cnt = 10;
+ while (cnt--) {
+ data = edp_read(EDP_BASE + EDP_MAINLINK_READY);
+ if (data & bit)
+ break;
+ }
+
+ if (cnt == 0)
+ dprintf(INFO, "%s: set link_train=%d failed\n", __func__, train);
+}
+
+char vm_pre_emphasis[4][4] = {
+ {0x03, 0x06, 0x09, 0x0C},
+ {0x03, 0x06, 0x09, 0xFF},
+ {0x03, 0x06, 0xFF, 0xFF},
+ {0x03, 0xFF, 0xFF, 0xFF}
+};
+
+char vm_voltage_swing[4][4] = {
+ {0x64, 0x68, 0x6A, 0x6E},
+ {0x68, 0x6A, 0x6E, 0xFF},
+ {0x6A, 0x6E, 0xFF, 0xFF},
+ {0x6E, 0xFF, 0xFF, 0xFF}
+};
+
+static void edp_voltage_pre_emphasise_set(struct edp_aux_ctrl *ep)
+{
+ int value0 = 0;
+ int value1 = 0;
+
+ dprintf(INFO, "voltage_pre_emphasis_set: v=%d p=%d\n", ep->v_level, ep->p_level);
+
+ value0 = vm_pre_emphasis[(int)(ep->v_level)][(int)(ep->p_level)];
+ value1 = vm_voltage_swing[(int)(ep->v_level)][(int)(ep->p_level)];
+
+ /* Configure host and panel only if both values are allowed */
+ if (value0 != 0xFF && value1 != 0xFF) {
+ edp_write(EDP_BASE + EDP_PHY_EDPPHY_GLB_VM_CFG0, value0);
+ edp_write(EDP_BASE + EDP_PHY_EDPPHY_GLB_VM_CFG1, value1);
+ dprintf(INFO, "voltage_pre_emphasis_set: value0=0x%x value1=0x%x\n",
+ value0, value1);
+ edp_lane_set_write(ep, ep->v_level, ep->p_level);
+ }
+
+}
+
+static int edp_start_link_train_1(struct edp_aux_ctrl *ep)
+{
+ int tries, old_v_level;
+ int ret = 0;
+
+ dprintf(INFO, "link_train_1\n");
+
+ edp_host_train_set(ep, 0x01); /* train_1 */
+ edp_voltage_pre_emphasise_set(ep);
+ edp_train_pattern_set_write(ep, 0x21); /* train_1 */
+
+ tries = 0;
+ old_v_level = ep->v_level;
+ while (1) {
+ udelay(ep->dpcd.training_read_interval * 10);
+
+ edp_link_status_read(ep, 6);
+ if (edp_sink_clock_recovery_done(ep)) {
+ ret = 0;
+ break;
+ }
+
+ if (ep->v_level == DPCD_LINK_VOLTAGE_MAX) {
+ ret = -1;
+ break; /* quit */
+ }
+
+ if (old_v_level == ep->v_level) {
+ tries++;
+ if (tries >= 5) {
+ ret = -1;
+ break; /* quit */
+ }
+ } else {
+ tries = 0;
+ old_v_level = ep->v_level;
+ }
+
+ edp_sink_train_set_adjust(ep);
+ edp_voltage_pre_emphasise_set(ep);
+ }
+
+ return ret;
+}
+
+static int edp_start_link_train_2(struct edp_aux_ctrl *ep)
+{
+ int tries;
+ int ret = 0;
+ char pattern;
+
+ dprintf(INFO, "link_train_2\n");
+
+ if (ep->dpcd.flags & DPCD_TPS3)
+ pattern = 0x03;
+ else
+ pattern = 0x02;
+
+ edp_host_train_set(ep, pattern); /* train_2 */
+ edp_voltage_pre_emphasise_set(ep);
+ edp_train_pattern_set_write(ep, pattern | 0x20);/* train_2 */
+
+ tries = 0;
+ while (1) {
+ udelay(ep->dpcd.training_read_interval);
+
+ edp_link_status_read(ep, 6);
+
+ if (edp_sink_channel_eq_done(ep)) {
+ ret = 0;
+ break;
+ }
+
+ tries++;
+ if (tries > 5) {
+ ret = -1;
+ break;
+ }
+
+ edp_sink_train_set_adjust(ep);
+ edp_voltage_pre_emphasise_set(ep);
+ }
+
+ return ret;
+}
+
+static int edp_link_rate_shift(struct edp_aux_ctrl *ep)
+{
+ /* add calculation later */
+ return -1;
+}
+
+static void edp_clear_training_pattern(struct edp_aux_ctrl *ep)
+{
+ dprintf(INFO, "clear_training_pattern:\n");
+ edp_write(EDP_BASE + EDP_STATE_CTRL, 0);
+ edp_train_pattern_set_write(ep, 0);
+ udelay(ep->dpcd.training_read_interval);
+}
+
+static int edp_aux_link_train(struct edp_aux_ctrl *ep)
+{
+ int ret = 0;
+
+ dprintf(INFO, "%s:\n", __func__);
+ ret = edp_aux_chan_ready(ep);
+ if (ret == 0) {
+ dprintf(INFO, "link_train: LINK Train failed: aux chan NOT ready\n");
+ return ret;
+ }
+
+ /* start with max rate and lane */
+ ep->lane_cnt = ep->dpcd.max_lane_count;
+ ep->link_rate = ep->dpcd.max_link_rate;
+ edp_write(EDP_BASE + EDP_MAINLINK_CTRL, 0x1);
+
+train_start:
+ ep->v_level = 0; /* start from default level */
+ ep->p_level = 0;
+ edp_cap_lane_rate_set(ep);
+
+ edp_clear_training_pattern(ep);
+ udelay(ep->dpcd.training_read_interval);
+ edp_powerstate_write(ep, 1);
+
+ ret = edp_start_link_train_1(ep);
+ if (ret < 0) {
+ if (edp_link_rate_shift(ep) == 0) {
+ goto train_start;
+ } else {
+ dprintf(INFO, "Training 1 failed\n");
+ ret = -1;
+ goto clear;
+ }
+ }
+
+ dprintf(INFO, "%s: Training 1 completed successfully\n", __func__);
+
+ edp_clear_training_pattern(ep);
+ ret = edp_start_link_train_2(ep);
+ if (ret < 0) {
+ if (edp_link_rate_shift(ep) == 0) {
+ goto train_start;
+ } else {
+ dprintf(INFO, "Training 2 failed\n");
+ ret = -1;
+ goto clear;
+ }
+ }
+
+ dprintf(INFO, "%s: Training 2 completed successfully\n", __func__);
+
+clear:
+ edp_clear_training_pattern(ep);
+
+ return ret;
+}
+
+void mdss_edp_wait_for_hpd(void)
+{
+ while(1) {
+ udelay(1000);
+ edp_isr_poll();
+ if (edp_hpd_done) {
+ edp_hpd_done = 0;
+ break;
+ }
+ }
+}
+
+void mdss_edp_wait_for_video_ready(void)
+{
+ while(1) {
+ udelay(1000);
+ edp_isr_poll();
+ if (edp_video_ready) {
+ edp_video_ready = 0;
+ break;
+ }
+ }
+}
+
+void mdss_edp_dpcd_cap_read(void)
+{
+ edp_sink_capability_read(&edpctrl, 16);
+}
+void mdss_edp_pll_configure(void)
+{
+ struct display_timing_desc *dp;
+
+ dp = &edpctrl.edid.timing[0];
+ edp_pll_configure(dp->pclk);
+}
+
+void mdss_edp_lane_power_ctrl(int up)
+{
+
+ dprintf(SPEW, "%s: max_lane=%d\n", __func__, edpctrl.dpcd.max_lane_count);
+ edp_lane_power_ctrl(edpctrl.dpcd.max_lane_count, up);
+
+}
+
+void mdss_edp_dpcd_status_read(void)
+{
+ edp_link_status_read(&edpctrl, 6);
+}
+
+void mdss_edp_edid_read(void)
+{
+ edp_sink_edid_read(&edpctrl, 0);
+}
+
+int mdss_edp_link_train(void)
+{
+ return edp_aux_link_train(&edpctrl);
+}
+
+void mdss_edp_aux_init(void)
+{
+ edp_buf_init(&edpctrl.txp, edpctrl.txbuf, sizeof(edpctrl.txbuf));
+ edp_buf_init(&edpctrl.rxp, edpctrl.rxbuf, sizeof(edpctrl.rxbuf));
+}
diff --git a/platform/msm_shared/edp_phy.c b/platform/msm_shared/edp_phy.c
index 32ca68e..decef57 100644
--- a/platform/msm_shared/edp_phy.c
+++ b/platform/msm_shared/edp_phy.c
@@ -27,9 +27,75 @@
* SUCH DAMAGE.
*/
#include "edp.h"
-#include <platform/timer.h>
/* EDP phy configuration settings */
+
+void edp_phy_pll_reset(void)
+{
+ /* EDP_PHY_CTRL */
+ edp_write(EDP_BASE + 0x74, 0x005); /* bit 0, 2 */
+ dmb();
+ udelay(1);
+ edp_write(EDP_BASE + 0x74, 0x000); /* EDP_PHY_CTRL */
+}
+
+void edp_mainlink_reset(void)
+{
+ edp_write(EDP_BASE + 0x04, 0x02 ); /* EDP_MAINLINK_CTRL */
+ dmb();
+ udelay(1);
+ edp_write(EDP_BASE + 0x04, 0 ); /* EDP_MAINLINK_CTRL */
+}
+
+void edp_aux_reset(void)
+{
+ /*reset AUX */
+ edp_write(EDP_BASE + 0x300, BIT(1)); /* EDP_AUX_CTRL */
+ dmb();
+ udelay(1);
+ edp_write(EDP_BASE + 0x300, 0); /* EDP_AUX_CTRL */
+}
+
+void edp_aux_enable(void)
+{
+ edp_write(EDP_BASE + 0x300, 0x01); /* EDP_AUX_CTRL */
+ dmb();
+ udelay(1);
+}
+
+void edp_phy_powerup(int enable)
+{
+ if (enable) {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(EDP_BASE + 0x52c, 0x3f);
+ /* EDP_PHY_EDPPHY_GLB_CFG */
+ edp_write(EDP_BASE + 0x528, 0x1);
+ /* EDP_PHY_PLL_UNIPHY_PLL_GLB_CFG */
+ edp_write(EDP_BASE + 0x620, 0xf);
+ } else {
+ /* EDP_PHY_EDPPHY_GLB_PD_CTL */
+ edp_write(EDP_BASE + 0x52c, 0xc0);
+ edp_write(EDP_BASE + 0x620, 0x0);
+ }
+}
+
+void edp_lane_power_ctrl(int max_lane, int up)
+{
+ int i, off;
+ unsigned int data;
+
+ if (up)
+ data = 0; /* power up */
+ else
+ data = 0x7; /* power down */
+
+ /* EDP_PHY_EDPPHY_LNn_PD_CTL */
+ for (i = 0; i < max_lane; i++) {
+ off = 0x40 * i;
+ edp_write(EDP_BASE + 0x404 + off , data);
+ }
+}
+
void edp_phy_sw_reset(void)
{
/* phy sw reset */
@@ -62,6 +128,7 @@
edp_write(EDP_BASE + 0x620, 0xf);
/* EDP_AUX_CTRL */
ret = edp_read(EDP_BASE + 0x300);
+ ret = edp_read(EDP_BASE + 0x300);
edp_write(EDP_BASE + 0x300, ret | 0x1);
} else {
/* EDP_PHY_EDPPHY_GLB_PD_CTL */
@@ -69,37 +136,93 @@
}
}
-void edp_pll_configure(void)
+void edp_pll_configure(unsigned int rate)
{
- edp_write(EDP_BASE + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
- edp_write(EDP_BASE + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
- edp_write(EDP_BASE + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
- edp_write(EDP_BASE + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
- edp_write(EDP_BASE + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
- edp_write(EDP_BASE + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
- edp_write(EDP_BASE + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
- edp_write(EDP_BASE + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
- edp_write(EDP_BASE + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
- edp_write(EDP_BASE + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
- edp_write(EDP_BASE + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
- edp_write(EDP_BASE + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
- edp_write(EDP_BASE + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
- edp_write(EDP_BASE + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
- edp_write(EDP_BASE + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
- edp_write(EDP_BASE + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
- edp_write(EDP_BASE + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
- edp_write(EDP_BASE + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
- edp_write(EDP_BASE + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
- edp_write(EDP_BASE + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
- edp_write(EDP_BASE + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
- edp_write(EDP_BASE + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
- edp_write(EDP_BASE + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
- edp_write(EDP_BASE + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
- edp_write(EDP_BASE + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
- edp_write(EDP_BASE + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
- edp_write(EDP_BASE + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
- edp_write(EDP_BASE + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
+ if (rate == 810000000) {
+ edp_write(EDP_BASE + 0x60c, 0x18);
+ edp_write(EDP_BASE + 0x664, 0x5);
+ edp_write(EDP_BASE + 0x600, 0x0);
+ edp_write(EDP_BASE + 0x638, 0x36);
+ edp_write(EDP_BASE + 0x63c, 0x69);
+ edp_write(EDP_BASE + 0x640, 0xff);
+ edp_write(EDP_BASE + 0x644, 0x2f);
+ edp_write(EDP_BASE + 0x648, 0x0);
+ edp_write(EDP_BASE + 0x66c, 0x0a);
+ edp_write(EDP_BASE + 0x674, 0x01);
+ edp_write(EDP_BASE + 0x684, 0x5a);
+ edp_write(EDP_BASE + 0x688, 0x0);
+ edp_write(EDP_BASE + 0x68c, 0x60);
+ edp_write(EDP_BASE + 0x690, 0x0);
+ edp_write(EDP_BASE + 0x694, 0x2a);
+ edp_write(EDP_BASE + 0x698, 0x3);
+ edp_write(EDP_BASE + 0x65c, 0x10);
+ edp_write(EDP_BASE + 0x660, 0x1a);
+ edp_write(EDP_BASE + 0x604, 0x0);
+ edp_write(EDP_BASE + 0x624, 0x0);
+ edp_write(EDP_BASE + 0x628, 0x0);
+
+ edp_write(EDP_BASE + 0x620, 0x1);
+ edp_write(EDP_BASE + 0x620, 0x5);
+ edp_write(EDP_BASE + 0x620, 0x7);
+ edp_write(EDP_BASE + 0x620, 0xf);
+
+ } else if (rate == 138530000) {
+ edp_write(EDP_BASE + 0x664, 0x5); /* UNIPHY_PLL_LKDET_CFG2 */
+ edp_write(EDP_BASE + 0x600, 0x1); /* UNIPHY_PLL_REFCLK_CFG */
+ edp_write(EDP_BASE + 0x638, 0x36); /* UNIPHY_PLL_SDM_CFG0 */
+ edp_write(EDP_BASE + 0x63c, 0x62); /* UNIPHY_PLL_SDM_CFG1 */
+ edp_write(EDP_BASE + 0x640, 0x0); /* UNIPHY_PLL_SDM_CFG2 */
+ edp_write(EDP_BASE + 0x644, 0x28); /* UNIPHY_PLL_SDM_CFG3 */
+ edp_write(EDP_BASE + 0x648, 0x0); /* UNIPHY_PLL_SDM_CFG4 */
+ edp_write(EDP_BASE + 0x64c, 0x80); /* UNIPHY_PLL_SSC_CFG0 */
+ edp_write(EDP_BASE + 0x650, 0x0); /* UNIPHY_PLL_SSC_CFG1 */
+ edp_write(EDP_BASE + 0x654, 0x0); /* UNIPHY_PLL_SSC_CFG2 */
+ edp_write(EDP_BASE + 0x658, 0x0); /* UNIPHY_PLL_SSC_CFG3 */
+ edp_write(EDP_BASE + 0x66c, 0xa); /* UNIPHY_PLL_CAL_CFG0 */
+ edp_write(EDP_BASE + 0x674, 0x1); /* UNIPHY_PLL_CAL_CFG2 */
+ edp_write(EDP_BASE + 0x684, 0x5a); /* UNIPHY_PLL_CAL_CFG6 */
+ edp_write(EDP_BASE + 0x688, 0x0); /* UNIPHY_PLL_CAL_CFG7 */
+ edp_write(EDP_BASE + 0x68c, 0x60); /* UNIPHY_PLL_CAL_CFG8 */
+ edp_write(EDP_BASE + 0x690, 0x0); /* UNIPHY_PLL_CAL_CFG9 */
+ edp_write(EDP_BASE + 0x694, 0x46); /* UNIPHY_PLL_CAL_CFG10 */
+ edp_write(EDP_BASE + 0x698, 0x5); /* UNIPHY_PLL_CAL_CFG11 */
+ edp_write(EDP_BASE + 0x65c, 0x10); /* UNIPHY_PLL_LKDET_CFG0 */
+ edp_write(EDP_BASE + 0x660, 0x1a); /* UNIPHY_PLL_LKDET_CFG1 */
+ edp_write(EDP_BASE + 0x604, 0x0); /* UNIPHY_PLL_POSTDIV1_CFG */
+ edp_write(EDP_BASE + 0x624, 0x0); /* UNIPHY_PLL_POSTDIV2_CFG */
+ edp_write(EDP_BASE + 0x628, 0x0); /* UNIPHY_PLL_POSTDIV3_CFG */
+
+ edp_write(EDP_BASE + 0x620, 0x1); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(EDP_BASE + 0x620, 0x5); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(EDP_BASE + 0x620, 0x7); /* UNIPHY_PLL_GLB_CFG */
+ edp_write(EDP_BASE + 0x620, 0xf); /* UNIPHY_PLL_GLB_CFG */
+ } else {
+ dprintf(INFO, "%s: rate=%d is NOT supported\n", __func__, rate);
+
+ }
+}
+
+
+int mdss_edp_phy_pll_ready(void)
+{
+ int cnt;
+ int status;
+
+ cnt = 10;
+ while(cnt--) {
+ status = edp_read(EDP_BASE + 0x6c0);
+ if (status & 0x01)
+ break;
+ udelay(100);
+ }
+
+ if(cnt == 0) {
+ dprintf("%s: PLL NOT ready\n", __func__);
+ return 0;
+ }
+ else
+ return 1;
}
void edp_enable_mainlink(int enable)
@@ -166,22 +289,36 @@
return;
}
- edp_write(EDPLINK_CFG_RCGR, (4 << 8)); /* CFG RCGR */
+ edp_write(EDPLINK_CFG_RCGR, (4 << 8) | 0x01); /* CFG RCGR */
edp_write(EDPLINK_CMD_RCGR, 3); /* CMD RCGR */
edp_write(MDSS_EDPLINK_CBCR, 1); /* CBCR */
}
+void edp_enable_aux_clk(int enable)
+{
+ if (!enable) {
+ edp_write(MDSS_EDPAUX_CBCR, 0); /* CBCR */
+ return;
+ }
+
+ edp_write(EDPAUX_CFG_RCGR, 0x01); /* CFG RCGR */
+
+ edp_write(MDSS_EDPAUX_CBCR, 1); /* CBCR */
+}
+
void edp_config_clk(void)
{
edp_enable_link_clk(1);
edp_enable_pixel_clk(1);
+ edp_enable_aux_clk(1);
}
void edp_unconfig_clk(void)
{
edp_enable_link_clk(0);
edp_enable_pixel_clk(0);
+ edp_enable_aux_clk(0);
}
void edp_phy_misc_cfg(void)
diff --git a/platform/msm_shared/edp_util.c b/platform/msm_shared/edp_util.c
new file mode 100644
index 0000000..f40abf9
--- /dev/null
+++ b/platform/msm_shared/edp_util.c
@@ -0,0 +1,459 @@
+/* Copyright (c) 2013, 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 BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE 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 "edp.h"
+
+extern struct edp_aux_ctrl edpctrl;
+
+extern int edp_hpd_done;
+extern int edp_video_ready;
+
+/*
+ * edp buffer operation
+ */
+char *edp_buf_init(struct edp_buf *eb, char *buf, int size)
+{
+ eb->start = buf;
+ eb->size = size;
+ eb->data = eb->start;
+ eb->end = eb->start + eb->size;
+ eb->len = 0;
+ eb->trans_num = 0;
+ eb->i2c = 0;
+ return eb->data;
+}
+
+static char *edp_buf_reset(struct edp_buf *eb)
+{
+ eb->data = eb->start;
+ eb->len = 0;
+ eb->trans_num = 0;
+ eb->i2c = 0;
+ return eb->data;
+}
+
+static char *edp_buf_push(struct edp_buf *eb, int len)
+{
+ eb->data += len;
+ eb->len += len;
+ return eb->data;
+}
+
+static int edp_buf_trailing(struct edp_buf *eb)
+{
+ return (int)(eb->end - eb->data);
+}
+
+/*
+ * edp aux edp_buf_add_cmd:
+ * NO native and i2c command mix allowed
+ */
+static int edp_buf_add_cmd(struct edp_buf *eb, struct edp_cmd *cmd)
+{
+ char data;
+ char *bp, *cp;
+ int i, len;
+
+ if (cmd->read) /* read */
+ len = 4;
+ else
+ len = cmd->len + 4;
+
+ if (edp_buf_trailing(eb) < len)
+ return 0;
+
+ /*
+ * cmd fifo only has depth of 144 bytes
+ * limit buf length to 128 bytes here
+ */
+ if ((eb->len + len) > 128)
+ return 0;
+
+ bp = eb->data;
+ data = cmd->addr >> 16;
+ data &= 0x0f; /* 4 addr bits */
+ if (cmd->read)
+ data |= BIT(4);
+ *bp++ = data;
+ *bp++ = cmd->addr >> 8;
+ *bp++ = cmd->addr;
+ *bp++ = cmd->len - 1;
+
+ if (!cmd->read) { /* write */
+ cp = cmd->datap;
+ for (i = 0; i < cmd->len; i++)
+ *bp++ = *cp++;
+ }
+ edp_buf_push(eb, len);
+
+ if (cmd->i2c)
+ eb->i2c++;
+
+ eb->trans_num++; /* Increase transaction number */
+
+ return cmd->len - 1;
+}
+
+static int edp_cmd_fifo_tx(struct edp_buf *tp)
+{
+ int data;
+ char *dp;
+ int len, cnt;
+
+ len = tp->len; /* total byte to cmd fifo */
+ if (len == 0)
+ return 0;
+
+ cnt = 0;
+ dp = tp->start;
+
+ while (cnt < len) {
+ data = *dp; /* data byte */
+ data <<= 8;
+ data &= 0x00ff00; /* index = 0, write */
+ if (cnt == 0)
+ data |= BIT(31); /* INDEX_WRITE */
+ dprintf(SPEW, "%s: data=%x\n",__func__, data);
+ edp_write(EDP_BASE + EDP_AUX_DATA, data);
+ cnt++;
+ dp++;
+ }
+
+ data = (tp->trans_num - 1);
+ if (tp->i2c)
+ data |= BIT(8); /* I2C */
+
+ data |= BIT(9); /* GO */
+ dprintf(SPEW, "%s: data=%x\n",__func__, data);
+ edp_write(EDP_BASE + EDP_AUX_TRANS_CTRL, data);
+
+ return tp->len;
+}
+
+static int edp_cmd_fifo_rx(struct edp_buf *rp, int len)
+{
+ int data;
+ char *dp;
+ int i;
+
+ data = 0; /* index = 0 */
+ data |= BIT(31); /* INDEX_WRITE */
+ data |= BIT(0); /* read */
+ edp_write(EDP_BASE + EDP_AUX_DATA, data);
+
+ dp = rp->data;
+
+ /* discard first byte */
+ data = edp_read(EDP_BASE + EDP_AUX_DATA);
+ for (i = 0; i < len; i++) {
+ data = edp_read(EDP_BASE + EDP_AUX_DATA);
+ dprintf(SPEW, "%s: data=%x\n", __func__, data);
+ *dp++ = (char)((data >> 8) & 0xff);
+ }
+
+ rp->len = len;
+ return len;
+}
+
+
+void edp_aux_native_handler(unsigned int isr)
+{
+
+ dprintf(SPEW, "%s: isr=%x\n", __func__, isr);
+
+ if (isr & EDP_INTR_AUX_I2C_DONE)
+ edpctrl.aux_error_num = EDP_AUX_ERR_NONE;
+ else if (isr & EDP_INTR_WRONG_ADDR)
+ edpctrl.aux_error_num = EDP_AUX_ERR_ADDR;
+ else if (isr & EDP_INTR_TIMEOUT)
+ edpctrl.aux_error_num = EDP_AUX_ERR_TOUT;
+ if (isr & EDP_INTR_NACK_DEFER)
+ edpctrl.aux_error_num = EDP_AUX_ERR_NACK;
+}
+
+void edp_aux_i2c_handler(unsigned int isr)
+{
+
+ dprintf(SPEW, "%s: isr=%x\n", __func__, isr);
+
+ if (isr & EDP_INTR_AUX_I2C_DONE) {
+ if (isr & (EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER))
+ edpctrl.aux_error_num = EDP_AUX_ERR_NACK;
+ else
+ edpctrl.aux_error_num = EDP_AUX_ERR_NONE;
+ } else {
+ if (isr & EDP_INTR_WRONG_ADDR)
+ edpctrl.aux_error_num = EDP_AUX_ERR_ADDR;
+ else if (isr & EDP_INTR_TIMEOUT)
+ edpctrl.aux_error_num = EDP_AUX_ERR_TOUT;
+ if (isr & EDP_INTR_NACK_DEFER)
+ edpctrl.aux_error_num = EDP_AUX_ERR_NACK;
+ if (isr & EDP_INTR_I2C_NACK)
+ edpctrl.aux_error_num = EDP_AUX_ERR_NACK;
+ if (isr & EDP_INTR_I2C_DEFER)
+ edpctrl.aux_error_num = EDP_AUX_ERR_NACK;
+ }
+}
+
+void mdss_edp_irq_enable(void)
+{
+ edp_write(EDP_BASE + 0x308, EDP_INTR_MASK1);
+ edp_write(EDP_BASE + 0x30c, EDP_INTR_MASK2);
+}
+
+void mdss_edp_irq_disable(void)
+{
+ edp_write(EDP_BASE + 0x308, 0);
+ edp_write(EDP_BASE + 0x30c, 0);
+}
+
+int edp_isr_read(unsigned int *isr1, unsigned int *isr2)
+{
+ unsigned int data1, data2, mask1, mask2;
+ unsigned int ack;
+
+ data1 = edp_read(EDP_BASE + 0x308);
+ data2 = edp_read(EDP_BASE + 0x30c);
+
+ if (data1 == 0 && data2 == 0)
+ return 0;
+
+ mask1 = data1 & EDP_INTR_MASK1;
+ mask2 = data2 & EDP_INTR_MASK2;
+
+ data1 &= ~mask1; /* remove masks bit */
+ data2 &= ~mask2;
+
+ dprintf(SPEW, "%s: isr=%x mask=%x isr2=%x mask2=%x\n",
+ __func__, data1, mask1, data2, mask2);
+
+ if (data1 == 0 && data2 == 0) /* no irq set */
+ return 0;
+
+ ack = data1 & EDP_INTR_STATUS1;
+ ack <<= 1; /* ack bits */
+ ack |= mask1;
+ edp_write(EDP_BASE + 0x308, ack);
+
+ ack = data2 & EDP_INTR_STATUS2;
+ ack <<= 1; /* ack bits */
+ ack |= mask2;
+ edp_write(EDP_BASE + 0x30c, ack);
+
+ if (data1 & EDP_INTR_HPD) {
+ edp_hpd_done++;
+ dprintf(INFO, "%s: got EDP_INTR_HOD\n", __func__);
+ data1 &= ~EDP_INTR_HPD;
+ }
+
+ if (data2 & EDP_INTR_READY_FOR_VIDEO) {
+ edp_video_ready++;
+ dprintf(INFO, "%s: got EDP_INTR_READY_FOR_VIDEO\n", __func__);
+ data2 &= ~EDP_INTR_READY_FOR_VIDEO;
+ }
+
+ if (data1 == 0 && data2 == 0) /* only hpd set */
+ return 0;
+
+ *isr1 = data1;
+ *isr2 = data2;
+
+ return 1;
+}
+
+void edp_isr_poll(void)
+{
+ int cnt;
+ unsigned int isr1, isr2;
+
+ isr1 = 0;
+ isr2 = 0;
+
+ /* one second loop here to cover
+ * the worst case for i2c edid 128 bytes read
+ */
+ cnt = 1000;
+ while(cnt--) {
+ if (edp_isr_read(&isr1, &isr2))
+ break;
+ udelay(1000);
+ }
+
+ if(cnt <= 0) {
+ dprintf(INFO, "%s: NO isr\n", __func__);
+ return;
+ }
+
+ dprintf(SPEW, "%s: isr1=%x isr2=%x\n", __func__, isr1, isr2);
+
+ if (isr2 & EDP_INTR_READY_FOR_VIDEO) {
+ }
+
+ if (isr1 && edpctrl.aux_cmd_busy) {
+ /* clear EDP_AUX_TRANS_CTRL */
+ edp_write(EDP_BASE + 0x318, 0);
+ /* read EDP_INTERRUPT_TRANS_NUM */
+ edpctrl.aux_trans_num = edp_read(EDP_BASE + 0x310);
+
+ if (edpctrl.aux_cmd_i2c)
+ edp_aux_i2c_handler(isr1);
+ else
+ edp_aux_native_handler(isr1);
+ }
+}
+
+int edp_aux_write_cmds(struct edp_aux_ctrl *ep,
+ struct edp_cmd *cmd)
+{
+ struct edp_cmd *cm;
+ struct edp_buf *tp;
+ int len, ret;
+
+ ep->aux_cmd_busy = 1;
+
+ tp = &ep->txp;
+ edp_buf_reset(tp);
+
+ cm = cmd;
+ while (cm) {
+ dprintf(SPEW, "%s: i2c=%d read=%d addr=%x len=%d next=%d\n",
+ __func__, cm->i2c, cm->read, cm->addr, cm->len, cm->next);
+ ret = edp_buf_add_cmd(tp, cm);
+ if (ret <= 0)
+ break;
+ if (cm->next == 0)
+ break;
+ cm++;
+ }
+
+ if (tp->i2c)
+ ep->aux_cmd_i2c = 1;
+ else
+ ep->aux_cmd_i2c = 0;
+
+ len = edp_cmd_fifo_tx(&ep->txp);
+
+ edp_isr_poll();
+
+ if (ep->aux_error_num == EDP_AUX_ERR_NONE)
+ ret = len;
+ else
+ ret = ep->aux_error_num;
+
+ ep->aux_cmd_busy = 0;
+ return ret;
+}
+
+int edp_aux_read_cmds(struct edp_aux_ctrl *ep,
+ struct edp_cmd *cmds)
+{
+ struct edp_cmd *cm;
+ struct edp_buf *tp;
+ struct edp_buf *rp;
+ int len, ret;
+
+ ep->aux_cmd_busy = 1;
+
+ tp = &ep->txp;
+ rp = &ep->rxp;
+ edp_buf_reset(tp);
+ edp_buf_reset(rp);
+
+ cm = cmds;
+ len = 0;
+ while (cm) {
+ dprintf(SPEW, "%s: i2c=%d read=%d addr=%x len=%d next=%d\n",
+ __func__, cm->i2c, cm->read, cm->addr, cm->len, cm->next);
+ ret = edp_buf_add_cmd(tp, cm);
+ len += cm->len;
+ if (ret <= 0)
+ break;
+ if (cm->next == 0)
+ break;
+ cm++;
+ }
+
+ if (tp->i2c)
+ ep->aux_cmd_i2c = 1;
+ else
+ ep->aux_cmd_i2c = 0;
+{
+ unsigned int isr1, isr2;
+
+ isr1 = edp_read(EDP_BASE + 0x308);
+ isr2 = edp_read(EDP_BASE + 0x30c);
+
+ if (isr1 != EDP_INTR_MASK1)
+ dprintf(INFO, "%s: BEFORE: isr1=%x isr2=%x\n", __func__, isr1, isr2);
+}
+
+ edp_cmd_fifo_tx(tp);
+
+ edp_isr_poll();
+
+ if (ep->aux_error_num == EDP_AUX_ERR_NONE)
+ ret = edp_cmd_fifo_rx(rp, len);
+ else
+ ret = ep->aux_error_num;
+
+ ep->aux_cmd_busy = 0;
+
+ return ret;
+}
+
+
+int edp_aux_write_buf(struct edp_aux_ctrl *ep, int addr,
+ char *buf, int len, int i2c)
+{
+ struct edp_cmd cmd;
+
+ cmd.read = 0;
+ cmd.i2c = i2c;
+ cmd.addr = addr;
+ cmd.datap = buf;
+ cmd.len = len & 0x0ff;
+ cmd.next = 0;
+
+ return edp_aux_write_cmds(ep, &cmd);
+}
+
+int edp_aux_read_buf(struct edp_aux_ctrl *ep, int addr,
+ int len, int i2c)
+{
+ struct edp_cmd cmd;
+
+ cmd.read = 1;
+ cmd.i2c = i2c;
+ cmd.addr = addr;
+ cmd.datap = NULL;
+ cmd.len = len & 0x0ff;
+ cmd.next = 0;
+
+ return edp_aux_read_cmds(ep, &cmd);
+}
diff --git a/platform/msm_shared/include/edp.h b/platform/msm_shared/include/edp.h
index 04a3e49..61a5755 100644
--- a/platform/msm_shared/include/edp.h
+++ b/platform/msm_shared/include/edp.h
@@ -29,27 +29,270 @@
#ifndef EDP_H
#define EDP_H
-#include "msm_panel.h"
#include <reg.h>
#include <debug.h>
#include <err.h>
#include <platform/iomap.h>
#include <platform/clock.h>
+#include <platform/timer.h>
+
+#include "msm_panel.h"
#define edp_read(offset) readl_relaxed((offset))
#define edp_write(offset, data) writel_relaxed((data), (offset))
+
+#define AUX_CMD_FIFO_LEN 144
+#define AUX_CMD_MAX 16
+#define AUX_CMD_I2C_MAX 128
+
+#define EDP_PORT_MAX 1
+#define EDP_SINK_CAP_LEN 16
+
+#define EDP_AUX_ERR_NONE 0
+#define EDP_AUX_ERR_ADDR -1
+#define EDP_AUX_ERR_TOUT -2
+#define EDP_AUX_ERR_NACK -3
+
+/* 4 bits of aux command */
+#define EDP_CMD_AUX_WRITE 0x8
+#define EDP_CMD_AUX_READ 0x9
+
+/* 4 bits of i2c command */
+#define EDP_CMD_I2C_MOT 0x4 /* i2c middle of transaction */
+#define EDP_CMD_I2C_WRITE 0x0
+#define EDP_CMD_I2C_READ 0x1
+#define EDP_CMD_I2C_STATUS 0x2 /* i2c write status request */
+
+/* cmd reply: bit 0, 1 for aux */
+#define EDP_AUX_ACK 0x0
+#define EDP_AUX_NACK 0x1
+#define EDP_AUX_DEFER 0x2
+
+/* cmd reply: bit 2, 3 for i2c */
+#define EDP_I2C_ACK 0x0
+#define EDP_I2C_NACK 0x4
+#define EDP_I2C_DEFER 0x8
+
+#define EDP_CMD_TIMEOUT 400 /* us */
+#define EDP_CMD_LEN 16
+
+
+/* isr */
+#define EDP_INTR_HPD BIT(0)
+#define EDP_INTR_AUX_I2C_DONE BIT(3)
+#define EDP_INTR_WRONG_ADDR BIT(6)
+#define EDP_INTR_TIMEOUT BIT(9)
+#define EDP_INTR_NACK_DEFER BIT(12)
+#define EDP_INTR_WRONG_DATA_CNT BIT(15)
+#define EDP_INTR_I2C_NACK BIT(18)
+#define EDP_INTR_I2C_DEFER BIT(21)
+#define EDP_INTR_PLL_UNLOCKED BIT(24)
+#define EDP_INTR_AUX_ERROR BIT(27)
+
+
+#define EDP_INTR_STATUS1 \
+ (EDP_INTR_HPD | EDP_INTR_AUX_I2C_DONE| \
+ EDP_INTR_WRONG_ADDR | EDP_INTR_TIMEOUT | \
+ EDP_INTR_NACK_DEFER | EDP_INTR_WRONG_DATA_CNT | \
+ EDP_INTR_I2C_NACK | EDP_INTR_I2C_DEFER | \
+ EDP_INTR_PLL_UNLOCKED | EDP_INTR_AUX_ERROR)
+
+#define EDP_INTR_MASK1 (EDP_INTR_STATUS1 << 2)
+
+
+#define EDP_INTR_READY_FOR_VIDEO BIT(0)
+#define EDP_INTR_IDLE_PATTERNs_SENT BIT(3)
+#define EDP_INTR_FRAME_END BIT(6)
+#define EDP_INTR_CRC_UPDATED BIT(9)
+
+#define EDP_INTR_STATUS2 \
+ (EDP_INTR_READY_FOR_VIDEO | EDP_INTR_IDLE_PATTERNs_SENT | \
+ EDP_INTR_FRAME_END | EDP_INTR_CRC_UPDATED)
+
+#define EDP_INTR_MASK2 (EDP_INTR_STATUS2 << 2)
+
+
+
+
+
+#define EDP_MAINLINK_CTRL 0x004
+#define EDP_STATE_CTRL 0x008
+#define EDP_MAINLINK_READY 0x084
+
+#define EDP_AUX_CTRL 0x300
+#define EDP_INTERRUPT_STATUS 0x308
+#define EDP_INTERRUPT_STATUS_2 0x30c
+#define EDP_AUX_DATA 0x314
+#define EDP_AUX_TRANS_CTRL 0x318
+#define EDP_AUX_STATUS 0x324
+
+#define EDP_PHY_EDPPHY_GLB_VM_CFG0 0x510
+#define EDP_PHY_EDPPHY_GLB_VM_CFG1 0x514
+
+struct edp_cmd{
+ char read; /* 1 == read, 0 == write */
+ char i2c; /* 1 == i2c cmd, 0 == native cmd */
+ int addr; /* 20 bits */
+ char *datap;
+ int len; /* len to be tx OR len to be rx for read */
+ char next; /* next command */
+};
+
+struct edp_buf {
+ char *start; /* buffer start addr */
+ char *end; /* buffer end addr */
+ int size; /* size of buffer */
+ char *data; /* data pointer */
+ int len; /* dara length */
+ char trans_num; /* transaction number */
+ char i2c; /* 1 == i2c cmd, 0 == native cmd */
+};
+
+#define DPCD_ENHANCED_FRAME BIT(0)
+#define DPCD_TPS3 BIT(1)
+#define DPCD_MAX_DOWNSPREAD_0_5 BIT(2)
+#define DPCD_NO_AUX_HANDSHAKE BIT(3)
+#define DPCD_PORT_0_EDID_PRESENTED BIT(4)
+
+
+
+#define DPCD_LINK_VOLTAGE_MAX 4
+#define DPCD_LINK_PRE_EMPHASIS_MAX 4
+
+struct dpcd_cap {
+ char major;
+ char minor;
+ char max_lane_count;
+ char num_rx_port;
+ char i2c_speed_ctrl;
+ char scrambler_reset;
+ char enhanced_frame;
+ int max_link_rate; /* 162, 270 and 540 Mb, divided by 10 */
+ int flags;
+ int rx_port0_buf_size;
+ int training_read_interval;/* us */
+};
+
+
+struct display_timing_desc {
+ uint32_t pclk;
+ uint32_t h_addressable; /* addressable + boder = active */
+ uint32_t h_border;
+ uint32_t h_blank; /* fporch + bporch + sync_pulse = blank */
+ uint32_t h_fporch;
+ uint32_t h_sync_pulse;
+ uint32_t v_addressable; /* addressable + boder = active */
+ uint32_t v_border;
+ uint32_t v_blank; /* fporch + bporch + sync_pulse = blank */
+ uint32_t v_fporch;
+ uint32_t v_sync_pulse;
+ uint32_t width_mm;
+ uint32_t height_mm;
+ uint32_t interlaced;
+ uint32_t stereo;
+ uint32_t sync_type;
+ uint32_t sync_separate;
+ uint32_t vsync_pol;
+ uint32_t hsync_pol;
+};
+
+
+struct edp_edid {
+ char id_name[4];
+ short id_product;
+ char version;
+ char revision;
+ char video_intf; /* edp == 0x5 */
+ char color_depth; /* 6, 8, 10, 12 and 14 bits */
+ char color_format; /* RGB 4:4:4, YCrCb 4:4:4, Ycrcb 4:2:2 */
+ char dpm; /* display power management */
+ char sync_digital; /* 1 = digital */
+ char sync_separate; /* 1 = separate */
+ char vsync_pol; /* 0 = negative, 1 = positive */
+ char hsync_pol; /* 0 = negative, 1 = positive */
+ char ext_block_cnt;
+ struct display_timing_desc timing[4];
+};
+
+struct dpcd_link_status {
+ char lane_01_status;
+ char lane_23_status;
+ char interlane_align_done;
+ char downstream_port_status_changed;
+ char link_status_updated;
+ char port_0_in_sync;
+ char port_1_in_sync;
+ char req_voltage_swing[4];
+ char req_pre_emphasis[4];
+};
+
+struct edp_aux_ctrl {
+ int aux_cmd_busy;
+ int aux_cmd_i2c;
+ int aux_trans_num;
+ int aux_error_num;
+ int aux_ctrl_reg;
+ struct edp_buf txp;
+ struct edp_buf rxp;
+ char txbuf[256];
+ char rxbuf[256];
+ struct dpcd_link_status link_status;
+ char link_rate;
+ char lane_cnt;
+ char v_level;
+ char p_level;
+
+ /* transfer unit */
+ char tu_desired;
+ char valid_boundary;
+ char delay_start;
+ int bpp;
+
+ struct edp_edid edid;
+ struct dpcd_cap dpcd;
+};
+
+
+void edp_phy_pll_reset(void);
+void edp_mainlink_reset(void);
+void edp_aux_reset(void);
+void edp_phy_powerup(int enable);
+void edp_lane_power_ctrl(int max_lane, int up);
+
void edp_phy_sw_reset(void);
-void edp_pll_configure(void);
+void edp_pll_configure(unsigned int rate);
void edp_enable_lane_bist(int lane, int enable);
void edp_enable_mainlink(int enable);
void edp_hw_powerup(int enable);
void edp_config_clk(void);
void edp_unconfig_clk(void);
void edp_phy_misc_cfg(void);
-void edp_edid2pinfo(struct edp_panel_data *edp_panel);
int edp_on(void);
int edp_off(void);
int edp_config(void *pdata);
+void mdss_edp_dpcd_cap_read(void);
+void mdss_edp_dpcd_status_read(void);
+void mdss_edp_edid_read(void);
+int mdss_edp_link_train(void);
+void mdss_edp_aux_init(void);
+void mdss_edp_irq_enable(void);
+void mdss_edp_irq_disable(void);
+void mdss_edp_wait_for_hpd(void);
+void mdss_edp_wait_for_video_ready(void);
+void mdss_edp_lane_power_ctrl(int up);
+int mdss_edp_phy_pll_ready(void);
+void mdss_edp_pll_configure(void);
+void edp_cap2pinfo(struct msm_panel_info *pinfo);
+void edp_edid2pinfo(struct msm_panel_info *pinfo);
+int edp_aux_write_cmds(struct edp_aux_ctrl *ep,
+ struct edp_cmd *cmd);
+int edp_aux_read_cmds(struct edp_aux_ctrl *ep,
+ struct edp_cmd *cmds);
+int edp_aux_write_buf(struct edp_aux_ctrl *ep, int addr,
+ char *buf, int len, int i2c);
+int edp_aux_read_buf(struct edp_aux_ctrl *ep, int addr,
+ int len, int i2c);
+char *edp_buf_init(struct edp_buf *eb, char *buf, int size);
#endif /* EDP_H */
diff --git a/platform/msm_shared/include/msm_panel.h b/platform/msm_shared/include/msm_panel.h
index 42f2b47..d888598 100755
--- a/platform/msm_shared/include/msm_panel.h
+++ b/platform/msm_shared/include/msm_panel.h
@@ -102,6 +102,7 @@
/* Pad height */
uint32_t yres_pad;
uint8_t dual_pipe;
+ uint8_t split_display;
uint8_t pipe_swap;
};
@@ -161,6 +162,11 @@
uint8_t broadcast;
};
+struct edp_panel_info {
+ int max_lane_count;
+ unsigned long max_link_clk;
+};
+
enum lvds_mode {
LVDS_SINGLE_CHANNEL_MODE,
LVDS_DUAL_CHANNEL_MODE,
@@ -188,9 +194,11 @@
struct mipi_panel_info mipi;
struct lvds_panel_info lvds;
struct hdmi_panel_info hdmi;
+ struct edp_panel_info edp;
int (*on) (void);
int (*off) (void);
+ int (*prepare) (void);
int (*early_config) (void *pdata);
int (*config) (void *pdata);
int (*rotate) (void);
@@ -208,7 +216,7 @@
int (*pll_clk_func) (int enable, struct msm_panel_info *);
};
-struct display_timing_desc {
+struct display_timing_desc_x {
uint32_t pclk;
uint32_t h_addressable; /* addressable + boder = active */
uint32_t h_border;
@@ -230,7 +238,7 @@
uint32_t hsync_pol;
};
-struct edp_edid {
+struct edp_edid_x {
char id_name[4];
short id_product;
char version;
@@ -244,20 +252,21 @@
char vsync_pol; /* 0 = negative, 1 = positive */
char hsync_pol; /* 0 = negative, 1 = positive */
char ext_block_cnt;
- struct display_timing_desc timing[4];
+ struct display_timing_desc_x timing[4];
};
-struct dpcd_cap {
+struct dpcd_cap_x {
char max_lane_count;
uint32_t max_link_clk; /* 162, 270 and 540 Mb, divided by 10 */
};
+
+
struct edp_panel_data {
struct msm_fb_panel_data *panel_data;
- struct edp_edid edid;
- struct dpcd_cap dpcd;
+ struct edp_edid_x edid;
+ struct dpcd_cap_x dpcd;
};
-
int msm_display_init(struct msm_fb_panel_data *pdata);
#endif
diff --git a/platform/msm_shared/mdp5.c b/platform/msm_shared/mdp5.c
index 1a190ab..314d19c 100644
--- a/platform/msm_shared/mdp5.c
+++ b/platform/msm_shared/mdp5.c
@@ -222,7 +222,7 @@
return ERR_INVALID_ARGS;
adjust_xres = pinfo->xres;
- if (pinfo->lcdc.dual_pipe) {
+ if (pinfo->lcdc.split_display) {
adjust_xres /= 2;
if (intf_base == MDP_INTF_1_BASE) {
writel(BIT(8), MDP_TG_SINK);
@@ -329,8 +329,11 @@
writel(0x100, MDP_VP_0_MIXER_1_BASE + LAYER_3_BLEND_OP);
writel(0xFF, MDP_VP_0_MIXER_1_BASE + LAYER_3_BLEND0_FG_ALPHA);
- /* Baselayer for layer mixer 0 */
- writel(0x04000, MDP_CTL_1_BASE + CTL_LAYER_1);
+ /* Baselayer for layer mixer 1 */
+ if (pinfo->lcdc.split_display)
+ writel(0x04000, MDP_CTL_1_BASE + CTL_LAYER_1);
+ else
+ writel(0x01000, MDP_CTL_0_BASE + CTL_LAYER_1);
}
}
@@ -390,10 +393,18 @@
writel(0x0E9, MDP_QOS_REMAPPER_CLASS_0);
mdss_rgb_pipe_config(fb, pinfo, MDP_VP_0_RGB_0_BASE);
+ if (pinfo->lcdc.dual_pipe)
+ mdss_rgb_pipe_config(fb, pinfo, MDP_VP_0_RGB_1_BASE);
+
mdss_layer_mixer_setup(fb, pinfo);
- writel(0x1F10, MDP_CTL_0_BASE + CTL_TOP);
+
+ if (pinfo->lcdc.dual_pipe)
+ writel(0x181F10, MDP_CTL_0_BASE + CTL_TOP);
+ else
+ writel(0x1F10, MDP_CTL_0_BASE + CTL_TOP);
+
writel(0x9, MDP_DISP_INTF_SEL);
writel(0x1111, MDP_VIDEO_INTF_UNDERFLOW_CTL);
writel(0x01, MDP_UPPER_NEW_ROI_PRIOR_RO_START);
@@ -507,5 +518,7 @@
writel(0x00000000, MDP_INTR_EN);
}
+ writel(0x00000000, MDP_INTR_EN);
+
return NO_ERROR;
}
diff --git a/platform/msm_shared/rules.mk b/platform/msm_shared/rules.mk
index 0ffa27c..c6bc02a 100755
--- a/platform/msm_shared/rules.mk
+++ b/platform/msm_shared/rules.mk
@@ -100,6 +100,8 @@
$(LOCAL_DIR)/gpio.o \
$(LOCAL_DIR)/dload_util.o \
$(LOCAL_DIR)/edp.o \
+ $(LOCAL_DIR)/edp_util.o \
+ $(LOCAL_DIR)/edp_aux.o \
$(LOCAL_DIR)/edp_phy.o
endif