| /* 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" |
| #include "mdp5.h" |
| |
| #define RGB_COMPONENTS 3 |
| #define MAX_NUMBER_EDP_LANES 4 |
| |
| struct edp_aux_ctrl edpctrl; |
| |
| static struct msm_panel_info *edp_pinfo; |
| |
| static void edp_config_ctrl(void) |
| { |
| struct edp_aux_ctrl *ep; |
| struct dpcd_cap *cap; |
| struct display_timing_desc *dp; |
| unsigned long data = 0; |
| |
| ep = &edpctrl; |
| |
| dp = &ep->edid.timing[0]; |
| |
| cap = &ep->dpcd; |
| |
| data = cap->max_lane_count - 1; |
| data <<= 4; |
| |
| if (cap->enhanced_frame) |
| data |= 0x40; |
| |
| if (ep->edid.color_depth == 8) { |
| /* 0 == 6 bits, 1 == 8 bits */ |
| data |= 0x100; /* bit 8 */ |
| } |
| |
| if (!dp->interlaced) /* progressive */ |
| data |= 0x04; |
| |
| data |= 0x03; /* sycn clock & static Mvid */ |
| |
| dprintf(SPEW, "%s: data=%x\n", __func__, data); |
| |
| edp_write(EDP_BASE + 0xc, data); /* EDP_CONFIGURATION_CTRL */ |
| } |
| |
| static void edp_config_sw_mvid_nvid(void) |
| { |
| edp_write(EDP_BASE + 0x14, 0x13b); /* EDP_SOFTWARE_MVID */ |
| edp_write(EDP_BASE + 0x18, 0x266); /* EDP_SOFTWARE_NVID */ |
| } |
| |
| void edp_clock_synchrous(void) |
| { |
| struct edp_aux_ctrl *ep; |
| unsigned long data; |
| unsigned long color; |
| |
| ep = &edpctrl; |
| |
| data = 1; /* sync */ |
| |
| /* only legacy rgb mode supported */ |
| color = 0; /* 6 bits */ |
| if (ep->edid.color_depth == 8) |
| color = 0x01; |
| else if (ep->edid.color_depth == 10) |
| color = 0x02; |
| else if (ep->edid.color_depth == 12) |
| color = 0x03; |
| else if (ep->edid.color_depth == 16) |
| color = 0x04; |
| |
| color <<= 5; /* bit 5 to bit 7 */ |
| |
| data |= color; |
| |
| dprintf(SPEW, "%s: data=%x\n", __func__, data); |
| |
| /* EDP_MISC1_MISC0 */ |
| edp_write(EDP_BASE + 0x2c, data); |
| } |
| |
| static void edp_config_tu(void) |
| { |
| /* 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 + 0x4, 0x01); /* EDP_MAINLINK_CTRL */ |
| } |
| |
| static void edp_disable(int enable) |
| { |
| edp_write(EDP_BASE + 0x8, 0x0); /* EDP_STATE_CTRL */ |
| edp_write(EDP_BASE + 0x4, 0x00); /* EDP_MAINLINK_CTRL */ |
| } |
| |
| int edp_on(void) |
| { |
| mdss_edp_pll_configure(); |
| mdss_edp_phy_pll_ready(); |
| edp_phy_vm_pe_init(); |
| edp_config_ctrl(); |
| edp_config_sw_mvid_nvid(); |
| edp_clock_synchrous(); |
| edp_config_timing(edp_pinfo); |
| edp_config_tu(); |
| |
| 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(SPEW, "%s:\n", __func__); |
| |
| return 0; |
| } |
| |
| int edp_off(void) |
| { |
| mdss_edp_irq_disable(); |
| edp_enable_mainlink(0); |
| edp_phy_pll_reset(); |
| edp_mainlink_reset(); |
| edp_aux_reset(); |
| |
| edp_disable(1); |
| edp_unconfig_clk(); |
| |
| mdss_edp_lane_power_ctrl(0); |
| edp_phy_powerup(0); |
| |
| dprintf(SPEW, "%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(SPEW, "%s:\n", __func__); |
| |
| return 0; |
| } |
| |
| void edp_panel_init(struct msm_panel_info *pinfo) |
| { |
| if (!pinfo) |
| return; |
| |
| pinfo->lcdc.dual_pipe = 0; |
| pinfo->lcdc.split_display = 0; |
| |
| edp_pinfo = pinfo; |
| edp_pinfo->on = edp_on; |
| edp_pinfo->off = edp_off; |
| edp_pinfo->prepare = edp_prepare; |
| } |