| /* Copyright (c) 2012-2019, 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 <linux/interrupt.h> |
| #include <linux/delay.h> |
| #include <linux/err.h> |
| #include <linux/io.h> |
| #include <linux/clk/msm-clk.h> |
| #include <linux/iopoll.h> |
| #include <linux/kthread.h> |
| |
| #include "mdss_dsi.h" |
| #include "mdss_edp.h" |
| #include "mdss_dsi_phy.h" |
| |
| #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_0 0x00 |
| #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_1 0x04 |
| #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_2 0x08 |
| #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_3 0x0c |
| #define MDSS_DSI_DSIPHY_REGULATOR_CTRL_4 0x10 |
| #define MDSS_DSI_DSIPHY_REGULATOR_CAL_PWR_CFG 0x18 |
| #define MDSS_DSI_DSIPHY_LDO_CNTRL 0x1dc |
| #define MDSS_DSI_DSIPHY_REGULATOR_TEST 0x294 |
| #define MDSS_DSI_DSIPHY_STRENGTH_CTRL_0 0x184 |
| #define MDSS_DSI_DSIPHY_STRENGTH_CTRL_1 0x188 |
| #define MDSS_DSI_DSIPHY_STRENGTH_CTRL_2 0x18c |
| #define MDSS_DSI_DSIPHY_TIMING_CTRL_0 0x140 |
| #define MDSS_DSI_DSIPHY_GLBL_TEST_CTRL 0x1d4 |
| #define MDSS_DSI_DSIPHY_CTRL_0 0x170 |
| #define MDSS_DSI_DSIPHY_CTRL_1 0x174 |
| |
| #define SW_RESET BIT(2) |
| #define SW_RESET_PLL BIT(0) |
| #define PWRDN_B BIT(7) |
| |
| /* 8996 */ |
| #define DATALANE_OFFSET_FROM_BASE_8996 0x100 |
| #define DSIPHY_CMN_PLL_CNTRL 0x0048 |
| #define DATALANE_SIZE_8996 0x80 |
| |
| #define DSIPHY_CMN_GLBL_TEST_CTRL 0x0018 |
| #define DSIPHY_CMN_CTRL_0 0x001c |
| #define DSIPHY_CMN_CTRL_1 0x0020 |
| #define DSIPHY_CMN_LDO_CNTRL 0x004c |
| #define DSIPHY_PLL_CLKBUFLR_EN 0x041c |
| #define DSIPHY_PLL_PLL_BANDGAP 0x0508 |
| |
| #define DSIPHY_LANE_STRENGTH_CTRL_1 0x003c |
| #define DSIPHY_LANE_VREG_CNTRL 0x0064 |
| |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL0 0x214 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL1 0x218 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL2 0x21C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL3 0x220 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL4 0x224 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL5 0x228 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL6 0x22C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL7 0x230 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL8 0x234 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL9 0x238 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL10 0x23C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL11 0x240 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL12 0x244 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL13 0x248 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL14 0x24C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL15 0x250 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL16 0x254 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL17 0x258 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL18 0x25C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x260 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL19 0x260 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL20 0x264 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL21 0x268 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL22 0x26C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL23 0x270 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL24 0x274 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL25 0x278 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL26 0x27C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL27 0x280 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL28 0x284 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL29 0x288 |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL30 0x28C |
| #define DSI_DYNAMIC_REFRESH_PLL_CTRL31 0x290 |
| #define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR 0x294 |
| #define DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2 0x298 |
| |
| #define DSIPHY_DLN0_CFG1 0x0104 |
| #define DSIPHY_DLN0_TIMING_CTRL_4 0x0118 |
| #define DSIPHY_DLN0_TIMING_CTRL_5 0x011C |
| #define DSIPHY_DLN0_TIMING_CTRL_6 0x0120 |
| #define DSIPHY_DLN0_TIMING_CTRL_7 0x0124 |
| #define DSIPHY_DLN0_TIMING_CTRL_8 0x0128 |
| |
| #define DSIPHY_DLN1_CFG1 0x0184 |
| #define DSIPHY_DLN1_TIMING_CTRL_4 0x0198 |
| #define DSIPHY_DLN1_TIMING_CTRL_5 0x019C |
| #define DSIPHY_DLN1_TIMING_CTRL_6 0x01A0 |
| #define DSIPHY_DLN1_TIMING_CTRL_7 0x01A4 |
| #define DSIPHY_DLN1_TIMING_CTRL_8 0x01A8 |
| |
| #define DSIPHY_DLN2_CFG1 0x0204 |
| #define DSIPHY_DLN2_TIMING_CTRL_4 0x0218 |
| #define DSIPHY_DLN2_TIMING_CTRL_5 0x021C |
| #define DSIPHY_DLN2_TIMING_CTRL_6 0x0220 |
| #define DSIPHY_DLN2_TIMING_CTRL_7 0x0224 |
| #define DSIPHY_DLN2_TIMING_CTRL_8 0x0228 |
| |
| #define DSIPHY_DLN3_CFG1 0x0284 |
| #define DSIPHY_DLN3_TIMING_CTRL_4 0x0298 |
| #define DSIPHY_DLN3_TIMING_CTRL_5 0x029C |
| #define DSIPHY_DLN3_TIMING_CTRL_6 0x02A0 |
| #define DSIPHY_DLN3_TIMING_CTRL_7 0x02A4 |
| #define DSIPHY_DLN3_TIMING_CTRL_8 0x02A8 |
| |
| #define DSIPHY_CKLN_CFG1 0x0304 |
| #define DSIPHY_CKLN_TIMING_CTRL_4 0x0318 |
| #define DSIPHY_CKLN_TIMING_CTRL_5 0x031C |
| #define DSIPHY_CKLN_TIMING_CTRL_6 0x0320 |
| #define DSIPHY_CKLN_TIMING_CTRL_7 0x0324 |
| #define DSIPHY_CKLN_TIMING_CTRL_8 0x0328 |
| |
| #define DSIPHY_PLL_RESETSM_CNTRL5 0x043c |
| |
| #define PLL_CALC_DATA(addr0, addr1, data0, data1) \ |
| (((data1) << 24) | ((((addr1)/4) & 0xFF) << 16) | \ |
| ((data0) << 8) | (((addr0)/4) & 0xFF)) |
| |
| #define MDSS_DYN_REF_REG_W(base, offset, addr0, addr1, data0, data1) \ |
| writel_relaxed(PLL_CALC_DATA(addr0, addr1, data0, data1), \ |
| (base) + (offset)) |
| |
| void mdss_dsi_dfps_config_8996(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_panel_data *pdata; |
| struct mdss_panel_info *pinfo; |
| struct mdss_dsi_phy_ctrl *pd; |
| int glbl_tst_cntrl = |
| MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_GLBL_TEST_CTRL); |
| |
| pdata = &ctrl->panel_data; |
| if (!pdata) { |
| pr_err("%s: Invalid panel data\n", __func__); |
| return; |
| } |
| pinfo = &pdata->panel_info; |
| pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| if (mdss_dsi_is_ctrl_clk_slave(ctrl)) { |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL0, |
| DSIPHY_DLN0_CFG1, DSIPHY_DLN1_CFG1, |
| 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL1, |
| DSIPHY_DLN2_CFG1, DSIPHY_DLN3_CFG1, |
| 0x0, 0x0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL2, |
| DSIPHY_CKLN_CFG1, DSIPHY_DLN0_TIMING_CTRL_4, |
| 0x0, pd->timing_8996[0]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL3, |
| DSIPHY_DLN1_TIMING_CTRL_4, |
| DSIPHY_DLN2_TIMING_CTRL_4, |
| pd->timing_8996[8], |
| pd->timing_8996[16]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL4, |
| DSIPHY_DLN3_TIMING_CTRL_4, |
| DSIPHY_CKLN_TIMING_CTRL_4, |
| pd->timing_8996[24], |
| pd->timing_8996[32]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL5, |
| DSIPHY_DLN0_TIMING_CTRL_5, |
| DSIPHY_DLN1_TIMING_CTRL_5, |
| pd->timing_8996[1], |
| pd->timing_8996[9]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL6, |
| DSIPHY_DLN2_TIMING_CTRL_5, |
| DSIPHY_DLN3_TIMING_CTRL_5, |
| pd->timing_8996[17], |
| pd->timing_8996[25]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL7, |
| DSIPHY_CKLN_TIMING_CTRL_5, |
| DSIPHY_DLN0_TIMING_CTRL_6, |
| pd->timing_8996[33], |
| pd->timing_8996[2]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL8, |
| DSIPHY_DLN1_TIMING_CTRL_6, |
| DSIPHY_DLN2_TIMING_CTRL_6, |
| pd->timing_8996[10], |
| pd->timing_8996[18]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL9, |
| DSIPHY_DLN3_TIMING_CTRL_6, |
| DSIPHY_CKLN_TIMING_CTRL_6, |
| pd->timing_8996[26], |
| pd->timing_8996[34]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL10, |
| DSIPHY_DLN0_TIMING_CTRL_7, |
| DSIPHY_DLN1_TIMING_CTRL_7, |
| pd->timing_8996[3], |
| pd->timing_8996[11]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL11, |
| DSIPHY_DLN2_TIMING_CTRL_7, |
| DSIPHY_DLN3_TIMING_CTRL_7, |
| pd->timing_8996[19], |
| pd->timing_8996[27]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL12, |
| DSIPHY_CKLN_TIMING_CTRL_7, |
| DSIPHY_DLN0_TIMING_CTRL_8, |
| pd->timing_8996[35], |
| pd->timing_8996[4]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL13, |
| DSIPHY_DLN1_TIMING_CTRL_8, |
| DSIPHY_DLN2_TIMING_CTRL_8, |
| pd->timing_8996[12], |
| pd->timing_8996[20]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL14, |
| DSIPHY_DLN3_TIMING_CTRL_8, |
| DSIPHY_CKLN_TIMING_CTRL_8, |
| pd->timing_8996[28], |
| pd->timing_8996[36]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL15, |
| 0x0110, 0x0110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL16, |
| 0x0110, 0x0110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL17, |
| 0x0110, 0x0110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL18, |
| 0x0110, 0x0110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL19, |
| 0x0110, 0x0110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL20, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL21, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL22, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL23, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL24, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL25, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL26, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL27, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL28, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL29, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL30, |
| 0x110, 0x110, 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL31, |
| 0x110, 0x110, 0, 0); |
| MIPI_OUTP(ctrl->ctrl_base + |
| DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR, 0x0); |
| MIPI_OUTP(ctrl->ctrl_base + |
| DSI_DYNAMIC_REFRESH_PLL_UPPER_ADDR2, 0x0); |
| } else { |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL0, |
| DSIPHY_CMN_GLBL_TEST_CTRL, |
| DSIPHY_PLL_PLL_BANDGAP, |
| glbl_tst_cntrl | BIT(1), 0x1); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL1, |
| DSIPHY_PLL_RESETSM_CNTRL5, |
| DSIPHY_PLL_PLL_BANDGAP, |
| 0x0D, 0x03); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL2, |
| DSIPHY_PLL_RESETSM_CNTRL5, |
| DSIPHY_CMN_PLL_CNTRL, |
| 0x1D, 0x00); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL3, |
| DSIPHY_CMN_CTRL_1, DSIPHY_DLN0_CFG1, |
| 0x20, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL4, |
| DSIPHY_DLN1_CFG1, DSIPHY_DLN2_CFG1, |
| 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL5, |
| DSIPHY_DLN3_CFG1, DSIPHY_CKLN_CFG1, |
| 0, 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL6, |
| DSIPHY_DLN0_TIMING_CTRL_4, |
| DSIPHY_DLN1_TIMING_CTRL_4, |
| pd->timing_8996[0], |
| pd->timing_8996[8]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL7, |
| DSIPHY_DLN2_TIMING_CTRL_4, |
| DSIPHY_DLN3_TIMING_CTRL_4, |
| pd->timing_8996[16], |
| pd->timing_8996[24]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL8, |
| DSIPHY_CKLN_TIMING_CTRL_4, |
| DSIPHY_DLN0_TIMING_CTRL_5, |
| pd->timing_8996[32], |
| pd->timing_8996[1]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL9, |
| DSIPHY_DLN1_TIMING_CTRL_5, |
| DSIPHY_DLN2_TIMING_CTRL_5, |
| pd->timing_8996[9], |
| pd->timing_8996[17]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL10, |
| DSIPHY_DLN3_TIMING_CTRL_5, |
| DSIPHY_CKLN_TIMING_CTRL_5, |
| pd->timing_8996[25], |
| pd->timing_8996[33]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL11, |
| DSIPHY_DLN0_TIMING_CTRL_6, |
| DSIPHY_DLN1_TIMING_CTRL_6, |
| pd->timing_8996[2], |
| pd->timing_8996[10]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL12, |
| DSIPHY_DLN2_TIMING_CTRL_6, |
| DSIPHY_DLN3_TIMING_CTRL_6, |
| pd->timing_8996[18], |
| pd->timing_8996[26]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL13, |
| DSIPHY_CKLN_TIMING_CTRL_6, |
| DSIPHY_DLN0_TIMING_CTRL_7, |
| pd->timing_8996[34], |
| pd->timing_8996[3]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL14, |
| DSIPHY_DLN1_TIMING_CTRL_7, |
| DSIPHY_DLN2_TIMING_CTRL_7, |
| pd->timing_8996[11], |
| pd->timing_8996[19]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL15, |
| DSIPHY_DLN3_TIMING_CTRL_7, |
| DSIPHY_CKLN_TIMING_CTRL_7, |
| pd->timing_8996[27], |
| pd->timing_8996[35]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL16, |
| DSIPHY_DLN0_TIMING_CTRL_8, |
| DSIPHY_DLN1_TIMING_CTRL_8, |
| pd->timing_8996[4], |
| pd->timing_8996[12]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL17, |
| DSIPHY_DLN2_TIMING_CTRL_8, |
| DSIPHY_DLN3_TIMING_CTRL_8, |
| pd->timing_8996[20], |
| pd->timing_8996[28]); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL18, |
| DSIPHY_CKLN_TIMING_CTRL_8, |
| DSIPHY_CMN_CTRL_1, |
| pd->timing_8996[36], 0); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL30, |
| DSIPHY_CMN_GLBL_TEST_CTRL, |
| DSIPHY_CMN_GLBL_TEST_CTRL, |
| ((glbl_tst_cntrl) & (~BIT(2))), |
| ((glbl_tst_cntrl) & (~BIT(2)))); |
| MDSS_DYN_REF_REG_W(ctrl->ctrl_base, |
| DSI_DYNAMIC_REFRESH_PLL_CTRL31, |
| DSIPHY_CMN_GLBL_TEST_CTRL, |
| DSIPHY_CMN_GLBL_TEST_CTRL, |
| ((glbl_tst_cntrl) & (~BIT(2))), |
| ((glbl_tst_cntrl) & (~BIT(2)))); |
| } |
| |
| wmb(); /* make sure phy timings are updated*/ |
| } |
| |
| void mdss_dsi_ctrl_phy_reset(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| /* start phy sw reset */ |
| MIPI_OUTP(ctrl->ctrl_base + 0x12c, 0x0001); |
| udelay(1000); |
| wmb(); /* make sure reset */ |
| /* end phy sw reset */ |
| MIPI_OUTP(ctrl->ctrl_base + 0x12c, 0x0000); |
| udelay(100); |
| wmb(); /* maek sure reset cleared */ |
| } |
| |
| int mdss_dsi_phy_pll_reset_status(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| int rc; |
| u32 val; |
| u32 const sleep_us = 10, timeout_us = 100; |
| |
| pr_debug("%s: polling for RESETSM_READY_STATUS.CORE_READY\n", |
| __func__); |
| rc = readl_poll_timeout(ctrl->phy_io.base + 0x4cc, val, |
| (val & 0x1), sleep_us, timeout_us); |
| |
| return rc; |
| } |
| |
| static void mdss_dsi_phy_sw_reset_sub(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_ctrl_pdata *sctrl = NULL; |
| struct dsi_shared_data *sdata; |
| struct mdss_dsi_ctrl_pdata *octrl; |
| u32 reg_val = 0; |
| |
| if (ctrl == NULL) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| sdata = ctrl->shared_data; |
| octrl = mdss_dsi_get_other_ctrl(ctrl); |
| |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) { |
| if (mdss_dsi_is_ctrl_clk_master(ctrl)) |
| sctrl = mdss_dsi_get_ctrl_clk_slave(); |
| else |
| return; |
| } |
| |
| /* |
| * For dual dsi case if we do DSI PHY sw reset, |
| * this will reset DSI PHY regulators also. |
| * Since DSI PHY regulator is shared among both |
| * the DSI controllers, we should not do DSI PHY |
| * sw reset when the other DSI controller is still |
| * active. |
| */ |
| mutex_lock(&sdata->phy_reg_lock); |
| if ((mdss_dsi_is_hw_config_dual(sdata) && |
| (octrl && octrl->is_phyreg_enabled))) { |
| /* start phy lane and HW reset */ |
| reg_val = MIPI_INP(ctrl->ctrl_base + 0x12c); |
| reg_val |= (BIT(16) | BIT(8)); |
| MIPI_OUTP(ctrl->ctrl_base + 0x12c, reg_val); |
| /* wait for 1ms as per HW design */ |
| usleep_range(1000, 2000); |
| /* ensure phy lane and HW reset starts */ |
| wmb(); |
| /* end phy lane and HW reset */ |
| reg_val = MIPI_INP(ctrl->ctrl_base + 0x12c); |
| reg_val &= ~(BIT(16) | BIT(8)); |
| MIPI_OUTP(ctrl->ctrl_base + 0x12c, reg_val); |
| /* wait for 100us as per HW design */ |
| usleep_range(100, 200); |
| /* ensure phy lane and HW reset ends */ |
| wmb(); |
| } else { |
| /* start phy sw reset */ |
| mdss_dsi_ctrl_phy_reset(ctrl); |
| if (sctrl) |
| mdss_dsi_ctrl_phy_reset(sctrl); |
| |
| } |
| mutex_unlock(&sdata->phy_reg_lock); |
| } |
| |
| void mdss_dsi_phy_sw_reset(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_ctrl_pdata *sctrl = NULL; |
| struct dsi_shared_data *sdata; |
| |
| if (ctrl == NULL) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| sdata = ctrl->shared_data; |
| |
| /* |
| * When operating in split display mode, make sure that the PHY reset |
| * is only done from the clock master. This will ensure that the PLL is |
| * off when PHY reset is called. |
| */ |
| if (mdss_dsi_is_ctrl_clk_slave(ctrl)) |
| return; |
| |
| mdss_dsi_phy_sw_reset_sub(ctrl); |
| |
| if (mdss_dsi_is_ctrl_clk_master(ctrl)) { |
| sctrl = mdss_dsi_get_ctrl_clk_slave(); |
| if (sctrl) |
| mdss_dsi_phy_sw_reset_sub(sctrl); |
| else |
| pr_warn("%s: unable to get slave ctrl\n", __func__); |
| } |
| |
| /* All other quirks go here */ |
| if ((sdata->hw_rev == MDSS_DSI_HW_REV_103) && |
| !mdss_dsi_is_hw_config_dual(sdata) && |
| mdss_dsi_is_right_ctrl(ctrl)) { |
| |
| /* |
| * phy sw reset will wipe out the pll settings for PLL. |
| * Need to explicitly turn off PLL1 if unused to avoid |
| * current leakage issues. |
| */ |
| if ((mdss_dsi_is_hw_config_split(sdata) || |
| mdss_dsi_is_pll_src_pll0(sdata)) && |
| ctrl->vco_dummy_clk) { |
| pr_debug("Turn off unused PLL1 registers\n"); |
| clk_set_rate(ctrl->vco_dummy_clk, 1); |
| } |
| } |
| } |
| |
| static void mdss_dsi_phy_regulator_disable(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| if (!ctrl) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) |
| return; |
| |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) |
| return; |
| |
| MIPI_OUTP(ctrl->phy_regulator_io.base + 0x018, 0x000); |
| } |
| |
| static void mdss_dsi_phy_shutdown(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| if (!ctrl) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) { |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_PLL_CLKBUFLR_EN, 0); |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_GLBL_TEST_CTRL, 0); |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0); |
| } else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) { |
| mdss_dsi_12nm_phy_shutdown(ctrl); |
| } else { |
| MIPI_OUTP(ctrl->phy_io.base + MDSS_DSI_DSIPHY_CTRL_0, 0x000); |
| } |
| } |
| |
| /** |
| * mdss_dsi_lp_cd_rx() -- enable LP and CD at receiving |
| * @ctrl: pointer to DSI controller structure |
| * |
| * LP: low power |
| * CD: contention detection |
| */ |
| void mdss_dsi_lp_cd_rx(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| |
| if (!ctrl) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_20) || |
| (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM)) |
| return; |
| |
| pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| MIPI_OUTP((ctrl->phy_io.base) + 0x0188, pd->strength[1]); |
| /* Strength ctrl 1, LP Rx + CD Rxcontention detection */ |
| wmb(); |
| } |
| |
| static void mdss_dsi_28nm_phy_regulator_enable( |
| struct mdss_dsi_ctrl_pdata *ctrl_pdata) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| |
| pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| if (pd->reg_ldo_mode) { |
| /* Regulator ctrl 0 */ |
| MIPI_OUTP(ctrl_pdata->phy_regulator_io.base, 0x0); |
| /* Regulator ctrl - CAL_PWR_CFG */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x18, pd->regulator[6]); |
| /* Add H/w recommended delay */ |
| udelay(1000); |
| /* Regulator ctrl - TEST */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x14, pd->regulator[5]); |
| /* Regulator ctrl 3 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0xc, pd->regulator[3]); |
| /* Regulator ctrl 2 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x8, pd->regulator[2]); |
| /* Regulator ctrl 1 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x4, pd->regulator[1]); |
| /* Regulator ctrl 4 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x10, pd->regulator[4]); |
| /* LDO ctrl */ |
| if ((ctrl_pdata->shared_data->hw_rev == |
| MDSS_DSI_HW_REV_103_1) |
| || (ctrl_pdata->shared_data->hw_rev == |
| MDSS_DSI_HW_REV_104_2)) |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x05); |
| else |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x0d); |
| } else { |
| /* Regulator ctrl 0 */ |
| MIPI_OUTP(ctrl_pdata->phy_regulator_io.base, |
| 0x0); |
| /* Regulator ctrl - CAL_PWR_CFG */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x18, pd->regulator[6]); |
| /* Add H/w recommended delay */ |
| udelay(1000); |
| /* Regulator ctrl 1 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x4, pd->regulator[1]); |
| /* Regulator ctrl 2 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x8, pd->regulator[2]); |
| /* Regulator ctrl 3 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0xc, pd->regulator[3]); |
| /* Regulator ctrl 4 */ |
| MIPI_OUTP((ctrl_pdata->phy_regulator_io.base) |
| + 0x10, pd->regulator[4]); |
| /* LDO ctrl */ |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x1dc, 0x00); |
| /* Regulator ctrl 0 */ |
| MIPI_OUTP(ctrl_pdata->phy_regulator_io.base, |
| pd->regulator[0]); |
| } |
| } |
| |
| static void mdss_dsi_28nm_phy_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| int i, off, ln, offset; |
| |
| if (!ctrl_pdata) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| /* Strength ctrl 0 for 28nm PHY*/ |
| if ((ctrl_pdata->shared_data->hw_rev <= MDSS_DSI_HW_REV_104_2) && |
| (ctrl_pdata->shared_data->hw_rev != MDSS_DSI_HW_REV_103)) { |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5b); |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0184, pd->strength[0]); |
| /* make sure PHY strength ctrl is set */ |
| wmb(); |
| } |
| |
| off = 0x0140; /* phy timing ctrl 0 - 11 */ |
| for (i = 0; i < 12; i++) { |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->timing[i]); |
| /* make sure phy timing register is programed */ |
| wmb(); |
| off += 4; |
| } |
| |
| /* 4 lanes + clk lane configuration */ |
| /* lane config n * (0 - 4) & DataPath setup */ |
| for (ln = 0; ln < 5; ln++) { |
| off = (ln * 0x40); |
| for (i = 0; i < 9; i++) { |
| offset = i + (ln * 9); |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + off, |
| pd->lanecfg[offset]); |
| /* make sure lane config register is programed */ |
| wmb(); |
| off += 4; |
| } |
| } |
| |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0180, 0x0a); |
| /* MMSS_DSI_0_PHY_DSIPHY_CTRL_4 */ |
| wmb(); |
| |
| /* DSI_0_PHY_DSIPHY_GLBL_TEST_CTRL */ |
| if (!mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data)) { |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x01); |
| } else { |
| if (((ctrl_pdata->panel_data).panel_info.pdest == DISPLAY_1) || |
| (ctrl_pdata->shared_data->hw_rev == MDSS_DSI_HW_REV_103_1)) |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x01); |
| else |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x01d4, 0x00); |
| } |
| /* ensure DSIPHY_GLBL_TEST_CTRL is set */ |
| wmb(); |
| |
| /* MMSS_DSI_0_PHY_DSIPHY_CTRL_0 */ |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + 0x0170, 0x5f); |
| /* make sure PHY lanes are powered on */ |
| wmb(); |
| |
| off = 0x01b4; /* phy BIST ctrl 0 - 5 */ |
| for (i = 0; i < 6; i++) { |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + off, pd->bistctrl[i]); |
| wmb(); /* make sure PHY bit control is configured */ |
| off += 4; |
| } |
| |
| } |
| |
| static void mdss_dsi_20nm_phy_regulator_enable(struct mdss_dsi_ctrl_pdata |
| *ctrl_pdata) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| void __iomem *phy_io_base; |
| |
| pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db); |
| phy_io_base = ctrl_pdata->phy_regulator_io.base; |
| |
| if (pd->regulator_len != 7) { |
| pr_err("%s: wrong regulator settings\n", __func__); |
| return; |
| } |
| |
| if (pd->reg_ldo_mode) { |
| MIPI_OUTP(ctrl_pdata->phy_io.base + MDSS_DSI_DSIPHY_LDO_CNTRL, |
| 0x1d); |
| } else { |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CTRL_1, |
| pd->regulator[1]); |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CTRL_2, |
| pd->regulator[2]); |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CTRL_3, |
| pd->regulator[3]); |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CTRL_4, |
| pd->regulator[4]); |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CAL_PWR_CFG, |
| pd->regulator[6]); |
| MIPI_OUTP(ctrl_pdata->phy_io.base + MDSS_DSI_DSIPHY_LDO_CNTRL, |
| 0x00); |
| MIPI_OUTP(phy_io_base + MDSS_DSI_DSIPHY_REGULATOR_CTRL_0, |
| pd->regulator[0]); |
| } |
| } |
| |
| static void mdss_dsi_20nm_phy_config(struct mdss_dsi_ctrl_pdata *ctrl_pdata) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| int i, off, ln, offset; |
| |
| pd = &(((ctrl_pdata->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| if (pd->strength_len != 2) { |
| pr_err("%s: wrong strength ctrl\n", __func__); |
| return; |
| } |
| |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + MDSS_DSI_DSIPHY_STRENGTH_CTRL_0, |
| pd->strength[0]); |
| |
| |
| if (!mdss_dsi_is_hw_config_dual(ctrl_pdata->shared_data)) { |
| if (mdss_dsi_is_hw_config_split(ctrl_pdata->shared_data) || |
| mdss_dsi_is_left_ctrl(ctrl_pdata) || |
| (mdss_dsi_is_right_ctrl(ctrl_pdata) && |
| mdss_dsi_is_pll_src_pll0(ctrl_pdata->shared_data))) |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + |
| MDSS_DSI_DSIPHY_GLBL_TEST_CTRL, 0x00); |
| else |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + |
| MDSS_DSI_DSIPHY_GLBL_TEST_CTRL, 0x01); |
| } else { |
| if (mdss_dsi_is_left_ctrl(ctrl_pdata)) |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + |
| MDSS_DSI_DSIPHY_GLBL_TEST_CTRL, 0x00); |
| else |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + |
| MDSS_DSI_DSIPHY_GLBL_TEST_CTRL, 0x01); |
| } |
| |
| if (pd->lanecfg_len != 45) { |
| pr_err("%s: wrong lane cfg\n", __func__); |
| return; |
| } |
| |
| /* 4 lanes + clk lane configuration */ |
| /* lane config n * (0 - 4) & DataPath setup */ |
| for (ln = 0; ln < 5; ln++) { |
| off = (ln * 0x40); |
| for (i = 0; i < 9; i++) { |
| offset = i + (ln * 9); |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + off, |
| pd->lanecfg[offset]); |
| /* make sure lane config register is programed */ |
| wmb(); |
| off += 4; |
| } |
| } |
| |
| off = 0; /* phy timing ctrl 0 - 11 */ |
| for (i = 0; i < 12; i++) { |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + |
| MDSS_DSI_DSIPHY_TIMING_CTRL_0 + off, pd->timing[i]); |
| wmb(); /* make sure phy timing register is programed */ |
| off += 4; |
| } |
| |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + MDSS_DSI_DSIPHY_CTRL_1, 0); |
| /* make sure everything is written before enable */ |
| wmb(); |
| MIPI_OUTP((ctrl_pdata->phy_io.base) + MDSS_DSI_DSIPHY_CTRL_0, 0x7f); |
| } |
| |
| static void mdss_dsi_8996_pll_source_standalone( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| u32 data; |
| |
| /* |
| * pll right output enabled |
| * bit clk select from left |
| */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_PLL_CLKBUFLR_EN, 0x01); |
| data = MIPI_INP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL); |
| data &= ~BIT(2); |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, data); |
| } |
| |
| static void mdss_dsi_8996_pll_source_from_right( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| u32 data; |
| |
| /* |
| * pll left + right output disabled |
| * bit clk select from right |
| */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_PLL_CLKBUFLR_EN, 0x00); |
| data = MIPI_INP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL); |
| data |= BIT(2); |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, data); |
| |
| /* enable bias current for pll1 during split display case */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_PLL_PLL_BANDGAP, 0x3); |
| } |
| |
| static void mdss_dsi_8996_pll_source_from_left( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| u32 data; |
| |
| /* |
| * pll left + right output enabled |
| * bit clk select from left |
| */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_PLL_CLKBUFLR_EN, 0x03); |
| data = MIPI_INP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL); |
| data &= ~BIT(2); |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, data); |
| } |
| |
| static void mdss_dsi_8996_phy_regulator_enable( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| int j, off, ln, cnt, ln_off; |
| char *ip; |
| void __iomem *base; |
| |
| pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); |
| /* 4 lanes + clk lane configuration */ |
| for (ln = 0; ln < 5; ln++) { |
| /* |
| * data lane offset from base: 0x100 |
| * data lane size: 0x80 |
| */ |
| base = ctrl->phy_io.base + |
| DATALANE_OFFSET_FROM_BASE_8996; |
| base += (ln * DATALANE_SIZE_8996); /* lane base */ |
| |
| /* vreg ctrl, 1 * 5 */ |
| cnt = 1; |
| ln_off = cnt * ln; |
| ip = &pd->regulator[ln_off]; |
| off = 0x64; |
| for (j = 0; j < cnt; j++, off += 4) |
| MIPI_OUTP(base + off, *ip++); |
| } |
| |
| wmb(); /* make sure registers committed */ |
| |
| } |
| |
| static void mdss_dsi_8996_phy_power_off( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| int ln; |
| void __iomem *base; |
| u32 data; |
| |
| /* Turn off PLL power */ |
| data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data & ~BIT(7)); |
| |
| /* 4 lanes + clk lane configuration */ |
| for (ln = 0; ln < 5; ln++) { |
| base = ctrl->phy_io.base + |
| DATALANE_OFFSET_FROM_BASE_8996; |
| base += (ln * DATALANE_SIZE_8996); /* lane base */ |
| |
| /* turn off phy ldo */ |
| MIPI_OUTP(base + DSIPHY_LANE_VREG_CNTRL, 0x1c); |
| } |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); |
| |
| /* 4 lanes + clk lane configuration */ |
| for (ln = 0; ln < 5; ln++) { |
| base = ctrl->phy_io.base + |
| DATALANE_OFFSET_FROM_BASE_8996; |
| base += (ln * DATALANE_SIZE_8996); /* lane base */ |
| |
| MIPI_OUTP(base + DSIPHY_LANE_STRENGTH_CTRL_1, 0x0); |
| } |
| |
| wmb(); /* make sure registers committed */ |
| } |
| |
| static void mdss_dsi_phy_power_off( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_panel_info *pinfo; |
| |
| if (ctrl->phy_power_off) |
| return; |
| |
| pinfo = &ctrl->panel_data.panel_info; |
| |
| if ((ctrl->shared_data->phy_rev != DSI_PHY_REV_20) || |
| !pinfo->allow_phy_power_off) { |
| pr_debug("%s: ctrl%d phy rev:%d panel support for phy off:%d\n", |
| __func__, ctrl->ndx, ctrl->shared_data->phy_rev, |
| pinfo->allow_phy_power_off); |
| return; |
| } |
| |
| /* supported for phy rev 2.0 and if panel allows it*/ |
| mdss_dsi_8996_phy_power_off(ctrl); |
| |
| ctrl->phy_power_off = true; |
| } |
| |
| static void mdss_dsi_8996_phy_power_on( |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| int j, off, ln, cnt, ln_off; |
| void __iomem *base; |
| struct mdss_dsi_phy_ctrl *pd; |
| char *ip; |
| u32 data; |
| |
| pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| /* 4 lanes + clk lane configuration */ |
| for (ln = 0; ln < 5; ln++) { |
| base = ctrl->phy_io.base + |
| DATALANE_OFFSET_FROM_BASE_8996; |
| base += (ln * DATALANE_SIZE_8996); /* lane base */ |
| |
| /* strength, 2 * 5 */ |
| cnt = 2; |
| ln_off = cnt * ln; |
| ip = &pd->strength[ln_off]; |
| off = 0x38; |
| for (j = 0; j < cnt; j++, off += 4) |
| MIPI_OUTP(base + off, *ip++); |
| } |
| |
| mdss_dsi_8996_phy_regulator_enable(ctrl); |
| |
| /* Turn on PLL power */ |
| data = MIPI_INP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0); |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, data | BIT(7)); |
| } |
| |
| static void mdss_dsi_phy_power_on( |
| struct mdss_dsi_ctrl_pdata *ctrl, bool mmss_clamp) |
| { |
| if (mmss_clamp && !ctrl->phy_power_off) |
| mdss_dsi_phy_init(ctrl); |
| else if ((ctrl->shared_data->phy_rev == DSI_PHY_REV_20) && |
| ctrl->phy_power_off) |
| mdss_dsi_8996_phy_power_on(ctrl); |
| |
| ctrl->phy_power_off = false; |
| } |
| |
| static void mdss_dsi_8996_phy_config(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_phy_ctrl *pd; |
| int j, off, ln, cnt, ln_off; |
| char *ip; |
| void __iomem *base; |
| |
| pd = &(((ctrl->panel_data).panel_info.mipi).dsi_phy_db); |
| |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_LDO_CNTRL, 0x1c); |
| |
| /* clk_en */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_GLBL_TEST_CTRL, 0x1); |
| |
| if (pd->lanecfg_len != 20) { |
| pr_err("%s: wrong lane cfg\n", __func__); |
| return; |
| } |
| |
| if (pd->strength_len != 10) { |
| pr_err("%s: wrong strength ctrl\n", __func__); |
| return; |
| } |
| |
| if (pd->regulator_len != 5) { |
| pr_err("%s: wrong regulator setting\n", __func__); |
| return; |
| } |
| |
| /* 4 lanes + clk lane configuration */ |
| for (ln = 0; ln < 5; ln++) { |
| /* |
| * data lane offset from base: 0x100 |
| * data lane size: 0x80 |
| */ |
| base = ctrl->phy_io.base + |
| DATALANE_OFFSET_FROM_BASE_8996; |
| base += (ln * DATALANE_SIZE_8996); /* lane base */ |
| |
| /* lane cfg, 4 * 5 */ |
| cnt = 4; |
| ln_off = cnt * ln; |
| ip = &pd->lanecfg[ln_off]; |
| off = 0x0; |
| for (j = 0; j < cnt; j++) { |
| MIPI_OUTP(base + off, *ip++); |
| off += 4; |
| } |
| |
| /* test str */ |
| MIPI_OUTP(base + 0x14, 0x0088); /* fixed */ |
| |
| /* phy timing, 8 * 5 */ |
| cnt = 8; |
| ln_off = cnt * ln; |
| ip = &pd->timing_8996[ln_off]; |
| off = 0x18; |
| for (j = 0; j < cnt; j++, off += 4) |
| MIPI_OUTP(base + off, *ip++); |
| |
| /* strength, 2 * 5 */ |
| cnt = 2; |
| ln_off = cnt * ln; |
| ip = &pd->strength[ln_off]; |
| off = 0x38; |
| for (j = 0; j < cnt; j++, off += 4) |
| MIPI_OUTP(base + off, *ip++); |
| } |
| |
| wmb(); /* make sure registers committed */ |
| |
| /* reset digital block */ |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_CTRL_1, 0x80); |
| udelay(100); |
| MIPI_OUTP((ctrl->phy_io.base) + DSIPHY_CMN_CTRL_1, 0x00); |
| |
| if (mdss_dsi_is_hw_config_split(ctrl->shared_data)) { |
| if (mdss_dsi_is_left_ctrl(ctrl)) |
| mdss_dsi_8996_pll_source_from_left(ctrl); |
| else |
| mdss_dsi_8996_pll_source_from_right(ctrl); |
| } else { |
| if (mdss_dsi_is_right_ctrl(ctrl) && |
| mdss_dsi_is_pll_src_pll0(ctrl->shared_data)) |
| mdss_dsi_8996_pll_source_from_left(ctrl); |
| else |
| mdss_dsi_8996_pll_source_standalone(ctrl); |
| } |
| |
| MIPI_OUTP(ctrl->phy_io.base + DSIPHY_CMN_CTRL_0, 0x7f); |
| wmb(); /* make sure registers committed */ |
| } |
| |
| static void mdss_dsi_phy_regulator_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, |
| bool enable) |
| { |
| struct mdss_dsi_ctrl_pdata *other_ctrl; |
| struct dsi_shared_data *sdata; |
| |
| if (!ctrl) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| sdata = ctrl->shared_data; |
| other_ctrl = mdss_dsi_get_other_ctrl(ctrl); |
| |
| mutex_lock(&sdata->phy_reg_lock); |
| if (enable) { |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) { |
| mdss_dsi_8996_phy_regulator_enable(ctrl); |
| } else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) { |
| mdss_dsi_12nm_phy_regulator_enable(ctrl); |
| } else { |
| switch (ctrl->shared_data->hw_rev) { |
| case MDSS_DSI_HW_REV_103: |
| mdss_dsi_20nm_phy_regulator_enable(ctrl); |
| break; |
| default: |
| /* |
| * For dual dsi case, do not reconfigure dsi phy |
| * regulator if the other dsi controller is still |
| * active. |
| */ |
| if (!mdss_dsi_is_hw_config_dual(sdata) || |
| (other_ctrl && (!other_ctrl->is_phyreg_enabled |
| || other_ctrl->mmss_clamp))) |
| mdss_dsi_28nm_phy_regulator_enable(ctrl); |
| break; |
| } |
| } |
| ctrl->is_phyreg_enabled = 1; |
| } else { |
| /* |
| * In split-dsi/dual-dsi configuration, the dsi phy regulator |
| * should be turned off only when both the DSI devices are |
| * going to be turned off since it is shared. |
| */ |
| if (mdss_dsi_is_hw_config_split(ctrl->shared_data) || |
| mdss_dsi_is_hw_config_dual(ctrl->shared_data)) { |
| if (other_ctrl && !other_ctrl->is_phyreg_enabled) |
| mdss_dsi_phy_regulator_disable(ctrl); |
| } else { |
| mdss_dsi_phy_regulator_disable(ctrl); |
| } |
| ctrl->is_phyreg_enabled = 0; |
| } |
| mutex_unlock(&sdata->phy_reg_lock); |
| } |
| |
| static void mdss_dsi_phy_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, bool enable) |
| { |
| struct mdss_dsi_ctrl_pdata *other_ctrl; |
| |
| if (!ctrl) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| if (enable) { |
| |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_20) { |
| mdss_dsi_8996_phy_config(ctrl); |
| } else if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) { |
| mdss_dsi_12nm_phy_config(ctrl); |
| } else { |
| switch (ctrl->shared_data->hw_rev) { |
| case MDSS_DSI_HW_REV_103: |
| mdss_dsi_20nm_phy_config(ctrl); |
| break; |
| default: |
| mdss_dsi_28nm_phy_config(ctrl); |
| break; |
| } |
| } |
| } else { |
| /* |
| * In split-dsi configuration, the phy should be disabled for |
| * the first controller only when the second controller is |
| * disabled. This is true regardless of whether broadcast |
| * mode is enabled. |
| */ |
| if (mdss_dsi_is_hw_config_split(ctrl->shared_data)) { |
| other_ctrl = mdss_dsi_get_other_ctrl(ctrl); |
| if (mdss_dsi_is_right_ctrl(ctrl) && other_ctrl) { |
| mdss_dsi_phy_shutdown(other_ctrl); |
| mdss_dsi_phy_shutdown(ctrl); |
| } |
| } else { |
| mdss_dsi_phy_shutdown(ctrl); |
| } |
| } |
| } |
| |
| void mdss_dsi_phy_disable(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| if (ctrl == NULL) { |
| pr_err("%s: Invalid input data\n", __func__); |
| return; |
| } |
| |
| mdss_dsi_phy_ctrl(ctrl, false); |
| mdss_dsi_phy_regulator_ctrl(ctrl, false); |
| /* |
| * Wait for the registers writes to complete in order to |
| * ensure that the phy is completely disabled |
| */ |
| wmb(); |
| } |
| |
| static void mdss_dsi_phy_init_sub(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| mdss_dsi_phy_regulator_ctrl(ctrl, true); |
| mdss_dsi_phy_ctrl(ctrl, true); |
| } |
| |
| void mdss_dsi_phy_init(struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct mdss_dsi_ctrl_pdata *sctrl = NULL; |
| |
| /* |
| * When operating in split display mode, make sure that both the PHY |
| * blocks are initialized together prior to the PLL being enabled. This |
| * is achieved by calling the phy_init function for the clk_slave from |
| * the clock_master. |
| */ |
| if (mdss_dsi_is_ctrl_clk_slave(ctrl)) |
| return; |
| |
| mdss_dsi_phy_init_sub(ctrl); |
| |
| if (mdss_dsi_is_ctrl_clk_master(ctrl)) { |
| sctrl = mdss_dsi_get_ctrl_clk_slave(); |
| if (sctrl) |
| mdss_dsi_phy_init_sub(sctrl); |
| else |
| pr_warn("%s: unable to get slave ctrl\n", __func__); |
| } |
| } |
| |
| static void mdss_dsi_phy_hstx_drv_ctrl( |
| struct mdss_dsi_ctrl_pdata *ctrl, bool enable) |
| { |
| if (ctrl->shared_data->phy_rev == DSI_PHY_REV_12NM) |
| mdss_dsi_12nm_phy_hstx_drv_ctrl(ctrl, enable); |
| } |
| |
| void mdss_dsi_core_clk_deinit(struct device *dev, struct dsi_shared_data *sdata) |
| { |
| if (sdata->mmss_misc_ahb_clk) |
| devm_clk_put(dev, sdata->mmss_misc_ahb_clk); |
| if (sdata->ext_pixel1_clk) |
| devm_clk_put(dev, sdata->ext_pixel1_clk); |
| if (sdata->ext_byte1_clk) |
| devm_clk_put(dev, sdata->ext_byte1_clk); |
| if (sdata->ext_pixel0_clk) |
| devm_clk_put(dev, sdata->ext_pixel0_clk); |
| if (sdata->ext_byte0_clk) |
| devm_clk_put(dev, sdata->ext_byte0_clk); |
| if (sdata->axi_clk) |
| devm_clk_put(dev, sdata->axi_clk); |
| if (sdata->ahb_clk) |
| devm_clk_put(dev, sdata->ahb_clk); |
| if (sdata->mdp_core_clk) |
| devm_clk_put(dev, sdata->mdp_core_clk); |
| } |
| |
| int mdss_dsi_clk_refresh(struct mdss_panel_data *pdata, bool update_phy) |
| { |
| struct mdss_dsi_ctrl_pdata *ctrl_pdata = NULL; |
| struct mdss_panel_info *pinfo = NULL; |
| int rc = 0; |
| |
| if (!pdata) { |
| pr_err("%s: invalid panel data\n", __func__); |
| return -EINVAL; |
| } |
| |
| ctrl_pdata = container_of(pdata, struct mdss_dsi_ctrl_pdata, |
| panel_data); |
| pinfo = &pdata->panel_info; |
| |
| if (!ctrl_pdata || !pinfo) { |
| pr_err("%s: invalid ctrl data\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (update_phy) { |
| pinfo->mipi.frame_rate = mdss_panel_calc_frame_rate(pinfo); |
| pr_debug("%s: new frame rate %d\n", |
| __func__, pinfo->mipi.frame_rate); |
| } |
| |
| rc = mdss_dsi_clk_div_config(&pdata->panel_info, |
| pdata->panel_info.mipi.frame_rate); |
| if (rc) { |
| pr_err("%s: unable to initialize the clk dividers\n", |
| __func__); |
| return rc; |
| } |
| ctrl_pdata->refresh_clk_rate = false; |
| ctrl_pdata->pclk_rate = pdata->panel_info.mipi.dsi_pclk_rate; |
| ctrl_pdata->byte_clk_rate = pdata->panel_info.clk_rate / 8; |
| pr_debug("%s ctrl_pdata->byte_clk_rate=%d ctrl_pdata->pclk_rate=%d\n", |
| __func__, ctrl_pdata->byte_clk_rate, ctrl_pdata->pclk_rate); |
| |
| rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle, |
| MDSS_DSI_LINK_BYTE_CLK, ctrl_pdata->byte_clk_rate, |
| MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); |
| if (rc) { |
| pr_err("%s: dsi_byte_clk - clk_set_rate failed\n", |
| __func__); |
| return rc; |
| } |
| |
| rc = mdss_dsi_clk_set_link_rate(ctrl_pdata->dsi_clk_handle, |
| MDSS_DSI_LINK_PIX_CLK, ctrl_pdata->pclk_rate, |
| MDSS_DSI_CLK_UPDATE_CLK_RATE_AT_ON); |
| if (rc) { |
| pr_err("%s: dsi_pixel_clk - clk_set_rate failed\n", |
| __func__); |
| return rc; |
| } |
| |
| if (update_phy) { |
| /* phy panel timing calaculation */ |
| rc = mdss_dsi_phy_calc_timing_param(pinfo, |
| ctrl_pdata->shared_data->phy_rev, |
| pinfo->mipi.frame_rate); |
| if (rc) { |
| pr_err("Error in calculating phy timings\n"); |
| return rc; |
| } |
| ctrl_pdata->update_phy_timing = false; |
| } |
| |
| return rc; |
| } |
| |
| int mdss_dsi_core_clk_init(struct platform_device *pdev, |
| struct dsi_shared_data *sdata) |
| { |
| struct device *dev = NULL; |
| int rc = 0; |
| |
| if (!pdev) { |
| pr_err("%s: Invalid pdev\n", __func__); |
| goto error; |
| } |
| |
| dev = &pdev->dev; |
| |
| /* Mandatory Clocks */ |
| sdata->mdp_core_clk = devm_clk_get(dev, "mdp_core_clk"); |
| if (IS_ERR(sdata->mdp_core_clk)) { |
| rc = PTR_ERR(sdata->mdp_core_clk); |
| pr_err("%s: Unable to get mdp core clk. rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| |
| sdata->ahb_clk = devm_clk_get(dev, "iface_clk"); |
| if (IS_ERR(sdata->ahb_clk)) { |
| rc = PTR_ERR(sdata->ahb_clk); |
| pr_err("%s: Unable to get mdss ahb clk. rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| |
| sdata->axi_clk = devm_clk_get(dev, "bus_clk"); |
| if (IS_ERR(sdata->axi_clk)) { |
| rc = PTR_ERR(sdata->axi_clk); |
| pr_err("%s: Unable to get axi bus clk. rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| |
| /* Optional Clocks */ |
| sdata->ext_byte0_clk = devm_clk_get(dev, "ext_byte0_clk"); |
| if (IS_ERR(sdata->ext_byte0_clk)) { |
| pr_debug("%s: unable to get byte0 clk rcg. rc=%d\n", |
| __func__, rc); |
| sdata->ext_byte0_clk = NULL; |
| } |
| |
| sdata->ext_pixel0_clk = devm_clk_get(dev, "ext_pixel0_clk"); |
| if (IS_ERR(sdata->ext_pixel0_clk)) { |
| pr_debug("%s: unable to get pixel0 clk rcg. rc=%d\n", |
| __func__, rc); |
| sdata->ext_pixel0_clk = NULL; |
| } |
| |
| sdata->ext_byte1_clk = devm_clk_get(dev, "ext_byte1_clk"); |
| if (IS_ERR(sdata->ext_byte1_clk)) { |
| pr_debug("%s: unable to get byte1 clk rcg. rc=%d\n", |
| __func__, rc); |
| sdata->ext_byte1_clk = NULL; |
| } |
| |
| sdata->ext_pixel1_clk = devm_clk_get(dev, "ext_pixel1_clk"); |
| if (IS_ERR(sdata->ext_pixel1_clk)) { |
| pr_debug("%s: unable to get pixel1 clk rcg. rc=%d\n", |
| __func__, rc); |
| sdata->ext_pixel1_clk = NULL; |
| } |
| |
| sdata->mmss_misc_ahb_clk = devm_clk_get(dev, "core_mmss_clk"); |
| if (IS_ERR(sdata->mmss_misc_ahb_clk)) { |
| sdata->mmss_misc_ahb_clk = NULL; |
| pr_debug("%s: Unable to get mmss misc ahb clk\n", |
| __func__); |
| } |
| |
| error: |
| if (rc) |
| mdss_dsi_core_clk_deinit(dev, sdata); |
| return rc; |
| } |
| |
| void mdss_dsi_link_clk_deinit(struct device *dev, |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| if (ctrl->vco_dummy_clk) |
| devm_clk_put(dev, ctrl->vco_dummy_clk); |
| if (ctrl->pixel_clk_rcg) |
| devm_clk_put(dev, ctrl->pixel_clk_rcg); |
| if (ctrl->byte_clk_rcg) |
| devm_clk_put(dev, ctrl->byte_clk_rcg); |
| if (ctrl->byte_clk) |
| devm_clk_put(dev, ctrl->byte_clk); |
| if (ctrl->esc_clk) |
| devm_clk_put(dev, ctrl->esc_clk); |
| if (ctrl->pixel_clk) |
| devm_clk_put(dev, ctrl->pixel_clk); |
| } |
| |
| int mdss_dsi_link_clk_init(struct platform_device *pdev, |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct device *dev = NULL; |
| int rc = 0; |
| |
| if (!pdev) { |
| pr_err("%s: Invalid pdev\n", __func__); |
| goto error; |
| } |
| |
| dev = &pdev->dev; |
| |
| /* Mandatory Clocks */ |
| ctrl->byte_clk = devm_clk_get(dev, "byte_clk"); |
| if (IS_ERR(ctrl->byte_clk)) { |
| rc = PTR_ERR(ctrl->byte_clk); |
| pr_err("%s: can't find dsi_byte_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->byte_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->pixel_clk = devm_clk_get(dev, "pixel_clk"); |
| if (IS_ERR(ctrl->pixel_clk)) { |
| rc = PTR_ERR(ctrl->pixel_clk); |
| pr_err("%s: can't find dsi_pixel_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->pixel_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->esc_clk = devm_clk_get(dev, "core_clk"); |
| if (IS_ERR(ctrl->esc_clk)) { |
| rc = PTR_ERR(ctrl->esc_clk); |
| pr_err("%s: can't find dsi_esc_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->esc_clk = NULL; |
| goto error; |
| } |
| |
| /* Optional Clocks */ |
| ctrl->byte_clk_rcg = devm_clk_get(dev, "byte_clk_rcg"); |
| if (IS_ERR(ctrl->byte_clk_rcg)) { |
| pr_debug("%s: can't find byte clk rcg. rc=%d\n", __func__, rc); |
| ctrl->byte_clk_rcg = NULL; |
| } |
| |
| ctrl->pixel_clk_rcg = devm_clk_get(dev, "pixel_clk_rcg"); |
| if (IS_ERR(ctrl->pixel_clk_rcg)) { |
| pr_debug("%s: can't find pixel clk rcg. rc=%d\n", __func__, rc); |
| ctrl->pixel_clk_rcg = NULL; |
| } |
| |
| ctrl->vco_dummy_clk = devm_clk_get(dev, "pll_vco_dummy_clk"); |
| if (IS_ERR(ctrl->vco_dummy_clk)) { |
| pr_debug("%s: can't find vco dummy clk. rc=%d\n", __func__, rc); |
| ctrl->vco_dummy_clk = NULL; |
| } |
| |
| error: |
| if (rc) |
| mdss_dsi_link_clk_deinit(dev, ctrl); |
| return rc; |
| } |
| |
| void mdss_dsi_shadow_clk_deinit(struct device *dev, |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| if (ctrl->mux_byte_clk) |
| devm_clk_put(dev, ctrl->mux_byte_clk); |
| if (ctrl->mux_pixel_clk) |
| devm_clk_put(dev, ctrl->mux_pixel_clk); |
| if (ctrl->pll_byte_clk) |
| devm_clk_put(dev, ctrl->pll_byte_clk); |
| if (ctrl->pll_pixel_clk) |
| devm_clk_put(dev, ctrl->pll_pixel_clk); |
| if (ctrl->shadow_byte_clk) |
| devm_clk_put(dev, ctrl->shadow_byte_clk); |
| if (ctrl->shadow_pixel_clk) |
| devm_clk_put(dev, ctrl->shadow_pixel_clk); |
| } |
| |
| int mdss_dsi_shadow_clk_init(struct platform_device *pdev, |
| struct mdss_dsi_ctrl_pdata *ctrl) |
| { |
| struct device *dev = NULL; |
| int rc = 0; |
| |
| if (!pdev) { |
| pr_err("%s: Invalid pdev\n", __func__); |
| return -EINVAL; |
| } |
| |
| dev = &pdev->dev; |
| ctrl->mux_byte_clk = devm_clk_get(dev, "pll_byte_clk_mux"); |
| if (IS_ERR(ctrl->mux_byte_clk)) { |
| rc = PTR_ERR(ctrl->mux_byte_clk); |
| pr_err("%s: can't find mux_byte_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->mux_byte_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->mux_pixel_clk = devm_clk_get(dev, "pll_pixel_clk_mux"); |
| if (IS_ERR(ctrl->mux_pixel_clk)) { |
| rc = PTR_ERR(ctrl->mux_pixel_clk); |
| pr_err("%s: can't find mdss_mux_pixel_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->mux_pixel_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->pll_byte_clk = devm_clk_get(dev, "pll_byte_clk_src"); |
| if (IS_ERR(ctrl->pll_byte_clk)) { |
| rc = PTR_ERR(ctrl->pll_byte_clk); |
| pr_err("%s: can't find pll_byte_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->pll_byte_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->pll_pixel_clk = devm_clk_get(dev, "pll_pixel_clk_src"); |
| if (IS_ERR(ctrl->pll_pixel_clk)) { |
| rc = PTR_ERR(ctrl->pll_pixel_clk); |
| pr_err("%s: can't find pll_pixel_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->pll_pixel_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->shadow_byte_clk = devm_clk_get(dev, "pll_shadow_byte_clk_src"); |
| if (IS_ERR(ctrl->shadow_byte_clk)) { |
| rc = PTR_ERR(ctrl->shadow_byte_clk); |
| pr_err("%s: can't find shadow_byte_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->shadow_byte_clk = NULL; |
| goto error; |
| } |
| |
| ctrl->shadow_pixel_clk = devm_clk_get(dev, "pll_shadow_pixel_clk_src"); |
| if (IS_ERR(ctrl->shadow_pixel_clk)) { |
| rc = PTR_ERR(ctrl->shadow_pixel_clk); |
| pr_err("%s: can't find shadow_pixel_clk. rc=%d\n", |
| __func__, rc); |
| ctrl->shadow_pixel_clk = NULL; |
| goto error; |
| } |
| |
| error: |
| if (rc) |
| mdss_dsi_shadow_clk_deinit(dev, ctrl); |
| return rc; |
| } |
| |
| bool is_diff_frame_rate(struct mdss_panel_info *panel_info, |
| u32 frame_rate) |
| { |
| if (panel_info->dynamic_fps && panel_info->current_fps) |
| return (frame_rate != panel_info->current_fps); |
| else |
| return (frame_rate != panel_info->mipi.frame_rate); |
| } |
| |
| int mdss_dsi_clk_div_config(struct mdss_panel_info *panel_info, |
| int frame_rate) |
| { |
| struct mdss_panel_data *pdata = container_of(panel_info, |
| struct mdss_panel_data, panel_info); |
| struct mdss_dsi_ctrl_pdata *ctrl_pdata = container_of(pdata, |
| struct mdss_dsi_ctrl_pdata, panel_data); |
| u64 h_period, v_period, clk_rate; |
| u32 dsi_pclk_rate; |
| u8 lanes = 0, bpp; |
| |
| if (panel_info->mipi.data_lane3) |
| lanes += 1; |
| if (panel_info->mipi.data_lane2) |
| lanes += 1; |
| if (panel_info->mipi.data_lane1) |
| lanes += 1; |
| if (panel_info->mipi.data_lane0) |
| lanes += 1; |
| |
| switch (panel_info->mipi.dst_format) { |
| case DSI_CMD_DST_FORMAT_RGB888: |
| case DSI_VIDEO_DST_FORMAT_RGB888: |
| case DSI_VIDEO_DST_FORMAT_RGB666_LOOSE: |
| bpp = 3; |
| break; |
| case DSI_CMD_DST_FORMAT_RGB565: |
| case DSI_VIDEO_DST_FORMAT_RGB565: |
| bpp = 2; |
| break; |
| default: |
| bpp = 3; /* Default format set to RGB888 */ |
| break; |
| } |
| |
| h_period = mdss_panel_get_htotal(panel_info, true); |
| v_period = mdss_panel_get_vtotal(panel_info); |
| |
| if (ctrl_pdata->refresh_clk_rate || is_diff_frame_rate(panel_info, |
| frame_rate) || (!panel_info->clk_rate)) { |
| if (lanes > 0) { |
| panel_info->clk_rate = h_period * v_period * frame_rate |
| * bpp * 8; |
| do_div(panel_info->clk_rate, lanes); |
| } else { |
| pr_err("%s: forcing mdss_dsi lanes to 1\n", __func__); |
| panel_info->clk_rate = |
| h_period * v_period * frame_rate * bpp * 8; |
| } |
| } |
| |
| if (panel_info->clk_rate == 0) |
| panel_info->clk_rate = 454000000; |
| |
| clk_rate = panel_info->clk_rate; |
| do_div(clk_rate, 8 * bpp); |
| dsi_pclk_rate = (u32) clk_rate * lanes; |
| |
| if ((dsi_pclk_rate < 3300000) || (dsi_pclk_rate > 250000000)) |
| dsi_pclk_rate = 35000000; |
| panel_info->mipi.dsi_pclk_rate = dsi_pclk_rate; |
| |
| return 0; |
| } |
| |
| static bool mdss_dsi_is_ulps_req_valid(struct mdss_dsi_ctrl_pdata *ctrl, |
| int enable, bool reconfig) |
| { |
| struct mdss_dsi_ctrl_pdata *octrl = NULL; |
| struct mdss_panel_data *pdata = &ctrl->panel_data; |
| struct mdss_panel_info *pinfo = &pdata->panel_info; |
| |
| pr_debug("%s: checking ulps req validity for ctrl%d\n", |
| __func__, ctrl->ndx); |
| |
| if (!mdss_dsi_ulps_feature_enabled(pdata) && |
| !pinfo->ulps_suspend_enabled) { |
| pr_debug("%s: ULPS feature is not enabled\n", __func__); |
| return false; |
| } |
| |
| /* |
| * No need to enter ULPS when transitioning from splash screen to |
| * boot animation since it is expected that the clocks would be turned |
| * right back on. |
| */ |
| if (enable && pinfo->cont_splash_enabled) { |
| pr_debug("%s: skip ULPS config with splash screen enabled\n", |
| __func__); |
| return false; |
| } |
| |
| /* |
| * No need to enable ULPS if panel is not yet initialized. |
| * However, this should be allowed in following usecases: |
| * 1. If ULPS during suspend feature is enabled, where we |
| * configure the lanes in ULPS after turning off the panel. |
| * 2. When coming out of idle PC with ULPS enabled, where we need to |
| * reconfigure the controller HW state again to ULPS prior to |
| * disabling ULPS. |
| */ |
| if (enable && !reconfig && |
| !(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT) && |
| !pdata->panel_info.ulps_suspend_enabled) { |
| pr_debug("%s: panel not yet initialized\n", __func__); |
| return false; |
| } |
| |
| /* |
| * For split-DSI usecase, wait till both controllers are initialized. |
| * The same exceptions as above are applicable here too. |
| */ |
| if (mdss_dsi_is_hw_config_split(ctrl->shared_data)) { |
| octrl = mdss_dsi_get_other_ctrl(ctrl); |
| if (enable && !ctrl->mmss_clamp && octrl && |
| !(octrl->ctrl_state & CTRL_STATE_PANEL_INIT) && |
| !pdata->panel_info.ulps_suspend_enabled) { |
| pr_debug("%s: split-DSI, other ctrl not ready yet\n", |
| __func__); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * mdss_dsi_ulps_config() - Program DSI lanes to enter/exit ULPS mode |
| * @ctrl: pointer to DSI controller structure |
| * @enable: 1 to enter ULPS, 0 to exit ULPS |
| * @reconfig: boolean to specify if DSI controller is reconfigured to enter ULPS |
| * |
| * This function executes the necessary programming sequence to enter/exit |
| * DSI Ultra-Low Power State (ULPS). This function assumes that the link and |
| * core clocks are already on. |
| */ |
| static int mdss_dsi_ulps_config(struct mdss_dsi_ctrl_pdata *ctrl, |
| int enable, bool reconfig) |
| { |
| int ret = 0; |
| struct mdss_panel_data *pdata = NULL; |
| struct mdss_panel_info *pinfo; |
| struct mipi_panel_info *mipi; |
| u32 lane_status = 0; |
| u32 active_lanes = 0; |
| |
| if (!ctrl) { |
| pr_err("%s: invalid input\n", __func__); |
| return -EINVAL; |
| } |
| |
| pdata = &ctrl->panel_data; |
| if (!pdata) { |
| pr_err("%s: Invalid panel data\n", __func__); |
| return -EINVAL; |
| } |
| pinfo = &pdata->panel_info; |
| mipi = &pinfo->mipi; |
| |
| if (!mdss_dsi_is_ulps_req_valid(ctrl, enable, reconfig)) { |
| pr_debug("%s: skiping ULPS config for ctrl%d, enable=%d\n", |
| __func__, ctrl->ndx, enable); |
| return 0; |
| } |
| |
| /* clock lane will always be programmed for ulps */ |
| active_lanes = BIT(4); |
| /* |
| * make a note of all active data lanes for which ulps entry/exit |
| * is needed |
| */ |
| if (mipi->data_lane0) |
| active_lanes |= BIT(0); |
| if (mipi->data_lane1) |
| active_lanes |= BIT(1); |
| if (mipi->data_lane2) |
| active_lanes |= BIT(2); |
| if (mipi->data_lane3) |
| active_lanes |= BIT(3); |
| |
| pr_debug("%s: configuring ulps (%s) for ctrl%d, active lanes=0x%08x,reconfig=%s\n", |
| __func__, (enable ? "on" : "off"), ctrl->ndx, |
| active_lanes, reconfig ? "true" : "false"); |
| |
| if (enable && !ctrl->ulps) { |
| /* |
| * Ensure that the lanes are idle prior to placing a ULPS entry |
| * request. This is needed to ensure that there is no overlap |
| * between any HS or LP commands being sent out on the lane and |
| * a potential ULPS entry request. |
| * |
| * This check needs to be avoided when we are resuming from idle |
| * power collapse and just restoring the controller state to |
| * ULPS with the clamps still in place. |
| */ |
| if (!reconfig) { |
| ret = mdss_dsi_wait_for_lane_idle(ctrl); |
| if (ret) { |
| pr_warn("%s: lanes not idle, skip ulps\n", |
| __func__); |
| ret = 0; |
| goto error; |
| } |
| } |
| |
| /* |
| * ULPS Entry Request. |
| * Wait for a short duration to ensure that the lanes |
| * enter ULP state. |
| */ |
| MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes); |
| usleep_range(100, 110); |
| |
| /* Check to make sure that all active data lanes are in ULPS */ |
| lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8); |
| if (lane_status & (active_lanes << 8)) { |
| pr_err("%s: ULPS entry req failed for ctrl%d. Lane status=0x%08x\n", |
| __func__, ctrl->ndx, lane_status); |
| ret = -EINVAL; |
| goto error; |
| } |
| |
| ctrl->ulps = true; |
| } else if (!enable && ctrl->ulps) { |
| /* |
| * Clear out any phy errors prior to exiting ULPS |
| * This fixes certain instances where phy does not exit |
| * ULPS cleanly. Also, do not print error during such cases. |
| */ |
| mdss_dsi_dln0_phy_err(ctrl, false); |
| |
| /* |
| * ULPS Exit Request |
| * Hardware requirement is to wait for at least 1ms |
| */ |
| MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 8); |
| usleep_range(1000, 1100); |
| |
| /* |
| * Sometimes when exiting ULPS, it is possible that some DSI |
| * lanes are not in the stop state which could lead to DSI |
| * commands not going through. To avoid this, force the lanes |
| * to be in stop state. |
| */ |
| MIPI_OUTP(ctrl->ctrl_base + 0x0AC, active_lanes << 16); |
| wmb(); /* ensure lanes are put to stop state */ |
| |
| MIPI_OUTP(ctrl->ctrl_base + 0x0AC, 0x0); |
| wmb(); /* ensure lanes are in proper state */ |
| |
| /* |
| * Wait for a short duration before enabling |
| * data transmission |
| */ |
| usleep_range(100, 110); |
| |
| lane_status = MIPI_INP(ctrl->ctrl_base + 0xA8); |
| ctrl->ulps = false; |
| } else { |
| pr_debug("%s: No change requested: %s -> %s\n", __func__, |
| ctrl->ulps ? "enabled" : "disabled", |
| enable ? "enabled" : "disabled"); |
| } |
| |
| pr_debug("%s: DSI lane status = 0x%08x. Ulps %s\n", __func__, |
| lane_status, enable ? "enabled" : "disabled"); |
| |
| error: |
| return ret; |
| } |
| |
| /** |
| * mdss_dsi_clamp_ctrl() - Program DSI clamps for supporting power collapse |
| * @ctrl: pointer to DSI controller structure |
| * @enable: 1 to enable clamps, 0 to disable clamps |
| * |
| * For idle-screen usecases with command mode panels, MDSS can be power |
| * collapsed. However, DSI phy needs to remain on. To avoid any mismatch |
| * between the DSI controller state, DSI phy needs to be clamped before |
| * power collapsing. This function executes the required programming |
| * sequence to configure these DSI clamps. This function should only be called |
| * when the DSI link clocks are disabled. |
| */ |
| static int mdss_dsi_clamp_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, int enable) |
| { |
| struct mipi_panel_info *mipi = NULL; |
| struct mdss_panel_data *pdata = NULL; |
| u32 clamp_reg, regval = 0; |
| u32 clamp_reg_off, phyrst_reg_off; |
| |
| if (!ctrl) { |
| pr_err("%s: invalid input\n", __func__); |
| return -EINVAL; |
| } |
| |
| if (!ctrl->mmss_misc_io.base) { |
| pr_err("%s: mmss_misc_io not mapped\n", __func__); |
| return -EINVAL; |
| } |
| |
| clamp_reg_off = ctrl->shared_data->ulps_clamp_ctrl_off; |
| phyrst_reg_off = ctrl->shared_data->ulps_phyrst_ctrl_off; |
| mipi = &ctrl->panel_data.panel_info.mipi; |
| pdata = &ctrl->panel_data; |
| |
| /* clock lane will always be clamped */ |
| clamp_reg = BIT(9); |
| if (ctrl->ulps) |
| clamp_reg |= BIT(8); |
| /* make a note of all active data lanes which need to be clamped */ |
| if (mipi->data_lane0) { |
| clamp_reg |= BIT(7); |
| if (ctrl->ulps) |
| clamp_reg |= BIT(6); |
| } |
| if (mipi->data_lane1) { |
| clamp_reg |= BIT(5); |
| if (ctrl->ulps) |
| clamp_reg |= BIT(4); |
| } |
| if (mipi->data_lane2) { |
| clamp_reg |= BIT(3); |
| if (ctrl->ulps) |
| clamp_reg |= BIT(2); |
| } |
| if (mipi->data_lane3) { |
| clamp_reg |= BIT(1); |
| if (ctrl->ulps) |
| clamp_reg |= BIT(0); |
| } |
| pr_debug("%s: called for ctrl%d, enable=%d, clamp_reg=0x%08x\n", |
| __func__, ctrl->ndx, enable, clamp_reg); |
| if (enable && !ctrl->mmss_clamp) { |
| if (!mdss_dsi_ulps_feature_enabled(pdata) && |
| !pdata->panel_info.ulps_suspend_enabled && |
| ctrl->shared_data->skip_clamp) { |
| |
| ctrl->mmss_clamp = true; |
| return 0; |
| } |
| |
| regval = MIPI_INP(ctrl->mmss_misc_io.base + clamp_reg_off); |
| /* Enable MMSS DSI Clamps */ |
| if (ctrl->ndx == DSI_CTRL_0) { |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval | clamp_reg); |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval | (clamp_reg | BIT(15))); |
| } else if (ctrl->ndx == DSI_CTRL_1) { |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval | (clamp_reg << 16)); |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval | ((clamp_reg << 16) | BIT(31))); |
| } |
| /* update clamp ctrl before setting phy reset disable */ |
| wmb(); |
| |
| /* |
| * This register write ensures that DSI PHY will not be |
| * reset when mdss ahb clock reset is asserted while coming |
| * out of power collapse |
| */ |
| if (IS_MDSS_MAJOR_MINOR_SAME(ctrl->shared_data->hw_rev, |
| MDSS_DSI_HW_REV_104) && |
| (MDSS_GET_STEP(ctrl->shared_data->hw_rev) != |
| MDSS_DSI_HW_REV_STEP_2)) { |
| |
| regval = MIPI_INP(ctrl->mmss_misc_io.base + |
| clamp_reg_off); |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval | BIT(30)); |
| } else { |
| MIPI_OUTP(ctrl->mmss_misc_io.base + phyrst_reg_off, |
| 0x1); |
| } |
| /* make sure that clamp ctrl is updated before disable call */ |
| wmb(); |
| ctrl->mmss_clamp = true; |
| } else if (!enable && ctrl->mmss_clamp) { |
| if (!mdss_dsi_ulps_feature_enabled(pdata) && |
| !pdata->panel_info.ulps_suspend_enabled && |
| ctrl->shared_data->skip_clamp) { |
| |
| ctrl->mmss_clamp = false; |
| return 0; |
| } |
| |
| if (IS_MDSS_MAJOR_MINOR_SAME(ctrl->shared_data->hw_rev, |
| MDSS_DSI_HW_REV_104) && |
| (MDSS_GET_STEP(ctrl->shared_data->hw_rev) != |
| MDSS_DSI_HW_REV_STEP_2)) { |
| |
| regval = MIPI_INP(ctrl->mmss_misc_io.base + |
| clamp_reg_off); |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval & ~BIT(30)); |
| } else { |
| MIPI_OUTP(ctrl->mmss_misc_io.base + phyrst_reg_off, |
| 0x0); |
| } |
| /* update clamp ctrl before unsetting phy reset disable */ |
| wmb(); |
| |
| regval = MIPI_INP(ctrl->mmss_misc_io.base + clamp_reg_off); |
| /* Disable MMSS DSI Clamps */ |
| if (ctrl->ndx == DSI_CTRL_0) |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval & ~(clamp_reg | BIT(15))); |
| else if (ctrl->ndx == DSI_CTRL_1) |
| MIPI_OUTP(ctrl->mmss_misc_io.base + clamp_reg_off, |
| regval & ~((clamp_reg << 16) | BIT(31))); |
| /* make sure that clamp ctrl is updated before enable call */ |
| wmb(); |
| ctrl->mmss_clamp = false; |
| } else { |
| pr_debug("%s: No change requested: %s -> %s\n", __func__, |
| ctrl->mmss_clamp ? "enabled" : "disabled", |
| enable ? "enabled" : "disabled"); |
| } |
| |
| return 0; |
| } |
| |
| DEFINE_MUTEX(dsi_clk_mutex); |
| |
| int mdss_dsi_clk_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, void *clk_handle, |
| enum mdss_dsi_clk_type clk_type, enum mdss_dsi_clk_state clk_state) |
| { |
| int rc = 0; |
| struct mdss_dsi_ctrl_pdata *mctrl = NULL; |
| int i, *vote_cnt = NULL; |
| |
| void *m_clk_handle; |
| bool is_ecg = false; |
| int state = MDSS_DSI_CLK_OFF; |
| |
| if (!ctrl) { |
| pr_err("%s: Invalid arg\n", __func__); |
| return -EINVAL; |
| } |
| |
| mutex_lock(&dsi_clk_mutex); |
| /* |
| * In sync_wait_broadcast mode, we need to enable clocks |
| * for the other controller as well when enabling clocks |
| * for the trigger controller. |
| * |
| * If sync wait_broadcase mode is not enabled, but if split display |
| * mode is enabled where both DSI controller's branch clocks are |
| * sourced out of a single PLL, then we need to ensure that the |
| * controller associated with that PLL also has it's clocks turned |
| * on. This is required to make sure that if that controller's PLL/PHY |
| * are clamped then they can be removed. |
| */ |
| if (mdss_dsi_sync_wait_trigger(ctrl)) { |
| mctrl = mdss_dsi_get_other_ctrl(ctrl); |
| if (!mctrl) |
| pr_warn("%s: Unable to get other control\n", __func__); |
| } else if (mdss_dsi_is_ctrl_clk_slave(ctrl)) { |
| mctrl = mdss_dsi_get_ctrl_clk_master(); |
| if (!mctrl) |
| pr_warn("%s: Unable to get clk master control\n", |
| __func__); |
| } |
| |
| /* |
| * it should add and remove extra votes based on voting clients to avoid |
| * removal of legitimate vote from DSI client. |
| */ |
| if (mctrl && (clk_handle == ctrl->dsi_clk_handle)) { |
| m_clk_handle = mctrl->dsi_clk_handle; |
| vote_cnt = &mctrl->m_dsi_vote_cnt; |
| } else if (mctrl) { |
| m_clk_handle = mctrl->mdp_clk_handle; |
| vote_cnt = &mctrl->m_mdp_vote_cnt; |
| } |
| |
| /* |
| * When DSI is used in split mode, the link clock for master controller |
| * has to be turned on first before the link clock for slave can be |
| * turned on. In case the current controller is a slave, an ON vote is |
| * cast for master before changing the state of the slave clock. After |
| * the state change for slave, the ON votes will be removed depending on |
| * the new state. |
| */ |
| pr_debug("%s: DSI_%d: clk = %d, state = %d, caller = %pS, mctrl=%d\n", |
| __func__, ctrl->ndx, clk_type, clk_state, |
| __builtin_return_address(0), mctrl ? 1 : 0); |
| if (mctrl && (clk_type & MDSS_DSI_LINK_CLK)) { |
| if (clk_state != MDSS_DSI_CLK_ON) { |
| /* preserve clk state; do not turn off forcefully */ |
| is_ecg = is_dsi_clk_in_ecg_state(m_clk_handle); |
| if (is_ecg) |
| state = MDSS_DSI_CLK_EARLY_GATE; |
| } |
| |
| rc = mdss_dsi_clk_req_state(m_clk_handle, |
| MDSS_DSI_ALL_CLKS, MDSS_DSI_CLK_ON, mctrl->ndx); |
| if (rc) { |
| pr_err("%s: failed to turn on mctrl clocks, rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| (*vote_cnt)++; |
| } |
| |
| rc = mdss_dsi_clk_req_state(clk_handle, clk_type, clk_state, ctrl->ndx); |
| if (rc) { |
| pr_err("%s: failed set clk state, rc = %d\n", __func__, rc); |
| goto error; |
| } |
| |
| if (mctrl && (clk_type & MDSS_DSI_LINK_CLK) && |
| clk_state != MDSS_DSI_CLK_ON) { |
| |
| /* |
| * In case of split dsi, an ON vote is cast for all state change |
| * requests. If the current state is ON, then the vote would not |
| * be released. |
| * |
| * If the current state is ECG, there is one possible way to |
| * transition in to this state, which is ON -> ECG. In this case |
| * two votes will be removed because one was cast at ON and |
| * other when entering ECG. |
| * |
| * If the current state is OFF, it could have been due to two |
| * possible transitions in to OFF state. |
| * 1. ON -> OFF: In this case two votes were cast by the |
| * slave controller, one during ON (which is not |
| * removed) and one during OFF. So we need to remove two |
| * votes. |
| * 2. ECG -> OFF: In this case there is only one vote |
| * for ON, since the previous ECG state must have |
| * removed two votes to let clocks turn off. |
| * |
| * To satisfy the above requirement, vote_cnt keeps track of |
| * the number of ON votes for master requested by slave. For |
| * every OFF/ECG state request, Either 2 or vote_cnt number of |
| * votes are removed depending on which is lower. |
| */ |
| for (i = 0; (i < *vote_cnt && i < 2); i++) { |
| rc = mdss_dsi_clk_req_state(m_clk_handle, |
| MDSS_DSI_ALL_CLKS, state, mctrl->ndx); |
| if (rc) { |
| pr_err("%s: failed to set mctrl clk state, rc = %d\n", |
| __func__, rc); |
| goto error; |
| } |
| } |
| (*vote_cnt) -= i; |
| pr_debug("%s: ctrl=%d, vote_cnt=%d dsi_vote_cnt=%d mdp_vote_cnt:%d\n", |
| __func__, ctrl->ndx, *vote_cnt, mctrl->m_dsi_vote_cnt, |
| mctrl->m_mdp_vote_cnt); |
| } |
| |
| error: |
| mutex_unlock(&dsi_clk_mutex); |
| return rc; |
| } |
| |
| int mdss_dsi_pre_clkoff_cb(void *priv, |
| enum mdss_dsi_clk_type clk, |
| enum mdss_dsi_lclk_type l_type, |
| enum mdss_dsi_clk_state new_state) |
| { |
| int rc = 0; |
| struct mdss_dsi_ctrl_pdata *ctrl = priv; |
| struct mdss_panel_data *pdata = NULL; |
| |
| pdata = &ctrl->panel_data; |
| |
| if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK) && |
| (new_state == MDSS_DSI_CLK_OFF)) { |
| /* Disable HS TX driver in DSI PHY if applicable */ |
| mdss_dsi_phy_hstx_drv_ctrl(ctrl, false); |
| } |
| |
| if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_LP_CLK) && |
| (new_state == MDSS_DSI_CLK_OFF)) { |
| /* |
| * If ULPS feature is enabled, enter ULPS first. |
| * However, when blanking the panel, we should enter ULPS |
| * only if ULPS during suspend feature is enabled. |
| */ |
| if (!(ctrl->ctrl_state & CTRL_STATE_PANEL_INIT)) { |
| if (pdata->panel_info.ulps_suspend_enabled) |
| mdss_dsi_ulps_config(ctrl, 1, false); |
| } else if (mdss_dsi_ulps_feature_enabled(pdata)) { |
| rc = mdss_dsi_ulps_config(ctrl, 1, false); |
| } |
| if (rc) { |
| pr_err("%s: failed enable ulps, rc = %d\n", |
| __func__, rc); |
| } |
| } |
| |
| if ((clk & MDSS_DSI_CORE_CLK) && (new_state == MDSS_DSI_CLK_OFF)) { |
| /* |
| * Enable DSI clamps only if entering idle power collapse or |
| * when ULPS during suspend is enabled. |
| */ |
| if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) || |
| pdata->panel_info.ulps_suspend_enabled) { |
| mdss_dsi_phy_power_off(ctrl); |
| rc = mdss_dsi_clamp_ctrl(ctrl, 1); |
| if (rc) |
| pr_err("%s: Failed to enable dsi clamps. rc=%d\n", |
| __func__, rc); |
| } else { |
| /* |
| * Make sure that controller is not in ULPS state when |
| * the DSI link is not active. |
| */ |
| rc = mdss_dsi_ulps_config(ctrl, 0, false); |
| if (rc) |
| pr_err("%s: failed to disable ulps. rc=%d\n", |
| __func__, rc); |
| } |
| } |
| |
| return rc; |
| } |
| |
| int mdss_dsi_post_clkon_cb(void *priv, |
| enum mdss_dsi_clk_type clk, |
| enum mdss_dsi_lclk_type l_type, |
| enum mdss_dsi_clk_state curr_state) |
| { |
| int rc = 0; |
| struct mdss_panel_data *pdata = NULL; |
| struct mdss_dsi_ctrl_pdata *ctrl = priv; |
| bool mmss_clamp; |
| |
| pdata = &ctrl->panel_data; |
| |
| if (clk & MDSS_DSI_CORE_CLK) { |
| mmss_clamp = ctrl->mmss_clamp; |
| /* |
| * controller setup is needed if coming out of idle |
| * power collapse with clamps enabled. |
| */ |
| if (mmss_clamp) |
| mdss_dsi_ctrl_setup(ctrl); |
| |
| rc = mdss_dsi_clamp_ctrl(ctrl, 0); |
| if (rc) { |
| pr_err("%s: Failed to disable dsi clamps. rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| |
| /* |
| * Phy setup is needed if coming out of idle |
| * power collapse with clamps enabled. |
| */ |
| if (ctrl->phy_power_off || mmss_clamp) |
| mdss_dsi_phy_power_on(ctrl, mmss_clamp); |
| } |
| |
| if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_LP_CLK)) { |
| if (ctrl->ulps) { |
| /* |
| * ULPS Entry Request. This is needed if the lanes were |
| * in ULPS prior to power collapse, since after |
| * power collapse and reset, the DSI controller resets |
| * back to idle state and not ULPS. This ulps entry |
| * request will transition the state of the DSI |
| * controller to ULPS which will match the state of the |
| * DSI phy. This needs to be done prior to disabling |
| * the DSI clamps. |
| * |
| * Also, reset the ulps flag so that ulps_config |
| * function would reconfigure the controller state to |
| * ULPS. |
| */ |
| ctrl->ulps = false; |
| rc = mdss_dsi_ulps_config(ctrl, 1, true); |
| if (rc) { |
| pr_err("%s: Failed to enter ULPS. rc=%d\n", |
| __func__, rc); |
| goto error; |
| } |
| |
| rc = mdss_dsi_ulps_config(ctrl, 0, false); |
| if (rc) { |
| pr_err("%s: failed to disable ulps, rc= %d\n", |
| __func__, rc); |
| goto error; |
| } |
| } |
| } |
| |
| /* Enable HS TX driver in DSI PHY if applicable */ |
| if ((clk & MDSS_DSI_LINK_CLK) && (l_type == MDSS_DSI_LINK_HS_CLK)) |
| mdss_dsi_phy_hstx_drv_ctrl(ctrl, true); |
| |
| error: |
| return rc; |
| } |
| |
| int mdss_dsi_post_clkoff_cb(void *priv, |
| enum mdss_dsi_clk_type clk_type, |
| enum mdss_dsi_lclk_type l_type, |
| enum mdss_dsi_clk_state curr_state) |
| { |
| int rc = 0; |
| struct mdss_dsi_ctrl_pdata *ctrl = priv; |
| struct mdss_panel_data *pdata = NULL; |
| struct dsi_shared_data *sdata; |
| int i; |
| |
| if (!ctrl) { |
| pr_err("%s: Invalid arg\n", __func__); |
| return -EINVAL; |
| } |
| |
| pdata = &ctrl->panel_data; |
| if ((clk_type & MDSS_DSI_CORE_CLK) && |
| (curr_state == MDSS_DSI_CLK_OFF)) { |
| sdata = ctrl->shared_data; |
| |
| for (i = DSI_MAX_PM - 1; i >= DSI_CORE_PM; i--) { |
| if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && |
| (i != DSI_CORE_PM)) |
| continue; |
| rc = msm_mdss_enable_vreg( |
| sdata->power_data[i].vreg_config, |
| sdata->power_data[i].num_vreg, 0); |
| if (rc) { |
| pr_warn("%s: failed to disable vregs for %s\n", |
| __func__, |
| __mdss_dsi_pm_name(i)); |
| rc = 0; |
| } else { |
| ctrl->core_power = false; |
| } |
| } |
| |
| /* |
| * temp workaround until framework issues pertaining to LP2 |
| * power state transitions are fixed. For now, we internally |
| * transition to LP2 state whenever core power is turned off |
| * in LP1 state |
| */ |
| if (mdss_dsi_is_panel_on_lp(pdata)) |
| mdss_dsi_panel_power_ctrl(pdata, |
| MDSS_PANEL_POWER_LP2); |
| } |
| return rc; |
| } |
| |
| int mdss_dsi_pre_clkon_cb(void *priv, |
| enum mdss_dsi_clk_type clk_type, |
| enum mdss_dsi_lclk_type l_type, |
| enum mdss_dsi_clk_state new_state) |
| { |
| int rc = 0; |
| struct mdss_dsi_ctrl_pdata *ctrl = priv; |
| struct mdss_panel_data *pdata = NULL; |
| struct dsi_shared_data *sdata; |
| int i; |
| |
| if (!ctrl) { |
| pr_err("%s: invalid input\n", __func__); |
| return -EINVAL; |
| } |
| |
| pdata = &ctrl->panel_data; |
| if ((clk_type & MDSS_DSI_CORE_CLK) && (new_state == MDSS_DSI_CLK_ON) && |
| (ctrl->core_power == false)) { |
| sdata = ctrl->shared_data; |
| /* |
| * Enable DSI core power |
| * 1.> PANEL_PM are controlled as part of |
| * panel_power_ctrl. Needed not be handled here. |
| * 2.> CORE_PM are controlled by dsi clk manager. |
| * 3.> CTRL_PM need to be enabled/disabled |
| * only during unblank/blank. Their state should |
| * not be changed during static screen. |
| */ |
| pr_debug("%s: Enable DSI core power\n", __func__); |
| for (i = DSI_CORE_PM; i < DSI_MAX_PM; i++) { |
| if ((ctrl->ctrl_state & CTRL_STATE_DSI_ACTIVE) && |
| (!pdata->panel_info.cont_splash_enabled) && |
| (i != DSI_CORE_PM)) |
| continue; |
| rc = msm_mdss_enable_vreg( |
| sdata->power_data[i].vreg_config, |
| sdata->power_data[i].num_vreg, 1); |
| if (rc) { |
| pr_err("%s: failed to enable vregs for %s\n", |
| __func__, |
| __mdss_dsi_pm_name(i)); |
| } else { |
| ctrl->core_power = true; |
| } |
| |
| } |
| /* |
| * temp workaround until framework issues pertaining to LP2 |
| * power state transitions are fixed. For now, if we intend to |
| * send a frame update when in LP1, we have to explicitly exit |
| * LP2 state here |
| */ |
| if (mdss_dsi_is_panel_on_ulp(pdata)) |
| mdss_dsi_panel_power_ctrl(pdata, MDSS_PANEL_POWER_LP1); |
| } |
| /* Disable dynamic clock gating*/ |
| if (ctrl->mdss_util->dyn_clk_gating_ctrl) |
| ctrl->mdss_util->dyn_clk_gating_ctrl(0); |
| |
| if ((clk_type & MDSS_DSI_LINK_CLK) && |
| (l_type == MDSS_DSI_LINK_HS_CLK)) { |
| u32 data = 0; |
| |
| data = MIPI_INP((ctrl->ctrl_io.base) + 0x0120); |
| /* |
| * For 12nm PHY, the PLL unlock bit in DSI_CLK_STATUS gets set |
| * when PLL is turned off. When device comes out of static |
| * screen without the DSI controller getting power collapsed, |
| * the bit might not clear sometimes. Clear the bit before |
| * turning ON the PLL. This avoids false error interrupt due to |
| * PLL unlocked bit after PLL is turned ON. |
| */ |
| if (data & BIT(16)) { |
| pr_debug("pll unlocked: 0x%x\n", data); |
| MIPI_OUTP((ctrl->ctrl_io.base) + 0x120, BIT(16)); |
| } |
| |
| } |
| |
| return rc; |
| } |
| |
| void mdss_edp_clk_deinit(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| if (edp_drv->aux_clk) |
| clk_put(edp_drv->aux_clk); |
| if (edp_drv->pixel_clk) |
| clk_put(edp_drv->pixel_clk); |
| if (edp_drv->ahb_clk) |
| clk_put(edp_drv->ahb_clk); |
| if (edp_drv->link_clk) |
| clk_put(edp_drv->link_clk); |
| if (edp_drv->mdp_core_clk) |
| clk_put(edp_drv->mdp_core_clk); |
| } |
| |
| int mdss_edp_clk_init(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| struct device *dev = &(edp_drv->pdev->dev); |
| |
| edp_drv->aux_clk = clk_get(dev, "core_clk"); |
| if (IS_ERR(edp_drv->aux_clk)) { |
| pr_err("%s: Can't find aux_clk", __func__); |
| edp_drv->aux_clk = NULL; |
| goto mdss_edp_clk_err; |
| } |
| |
| edp_drv->pixel_clk = clk_get(dev, "pixel_clk"); |
| if (IS_ERR(edp_drv->pixel_clk)) { |
| pr_err("%s: Can't find pixel_clk", __func__); |
| edp_drv->pixel_clk = NULL; |
| goto mdss_edp_clk_err; |
| } |
| |
| edp_drv->ahb_clk = clk_get(dev, "iface_clk"); |
| if (IS_ERR(edp_drv->ahb_clk)) { |
| pr_err("%s: Can't find ahb_clk", __func__); |
| edp_drv->ahb_clk = NULL; |
| goto mdss_edp_clk_err; |
| } |
| |
| edp_drv->link_clk = clk_get(dev, "link_clk"); |
| if (IS_ERR(edp_drv->link_clk)) { |
| pr_err("%s: Can't find link_clk", __func__); |
| edp_drv->link_clk = NULL; |
| goto mdss_edp_clk_err; |
| } |
| |
| /* need mdss clock to receive irq */ |
| edp_drv->mdp_core_clk = clk_get(dev, "mdp_core_clk"); |
| if (IS_ERR(edp_drv->mdp_core_clk)) { |
| pr_err("%s: Can't find mdp_core_clk", __func__); |
| edp_drv->mdp_core_clk = NULL; |
| goto mdss_edp_clk_err; |
| } |
| |
| return 0; |
| |
| mdss_edp_clk_err: |
| mdss_edp_clk_deinit(edp_drv); |
| return -EPERM; |
| } |
| |
| int mdss_edp_aux_clk_enable(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| int ret; |
| |
| if (clk_set_rate(edp_drv->aux_clk, 19200000) < 0) |
| pr_err("%s: aux_clk - clk_set_rate failed\n", |
| __func__); |
| |
| ret = clk_enable(edp_drv->aux_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable aux clk\n", __func__); |
| goto c2; |
| } |
| |
| ret = clk_enable(edp_drv->ahb_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable ahb clk\n", __func__); |
| goto c1; |
| } |
| |
| /* need mdss clock to receive irq */ |
| ret = clk_enable(edp_drv->mdp_core_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable mdp_core_clk\n", __func__); |
| goto c0; |
| } |
| |
| return 0; |
| c0: |
| clk_disable(edp_drv->ahb_clk); |
| c1: |
| clk_disable(edp_drv->aux_clk); |
| c2: |
| return ret; |
| |
| } |
| |
| void mdss_edp_aux_clk_disable(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| clk_disable(edp_drv->aux_clk); |
| clk_disable(edp_drv->ahb_clk); |
| clk_disable(edp_drv->mdp_core_clk); |
| } |
| |
| static void mdss_edp_clk_set_rate(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) |
| pr_err("%s: link_clk - clk_set_rate failed\n", |
| __func__); |
| |
| if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) |
| pr_err("%s: pixel_clk - clk_set_rate failed\n", |
| __func__); |
| } |
| |
| int mdss_edp_clk_enable(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| int ret; |
| |
| if (edp_drv->clk_on) { |
| pr_info("%s: edp clks are already ON\n", __func__); |
| return 0; |
| } |
| |
| if (clk_set_rate(edp_drv->link_clk, edp_drv->link_rate * 27000000) < 0) |
| pr_err("%s: link_clk - clk_set_rate failed\n", |
| __func__); |
| |
| if (clk_set_rate(edp_drv->aux_clk, edp_drv->aux_rate) < 0) |
| pr_err("%s: aux_clk - clk_set_rate failed\n", |
| __func__); |
| |
| if (clk_set_rate(edp_drv->pixel_clk, edp_drv->pixel_rate) < 0) |
| pr_err("%s: pixel_clk - clk_set_rate failed\n", |
| __func__); |
| |
| ret = clk_enable(edp_drv->aux_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable aux clk\n", __func__); |
| goto c4; |
| } |
| ret = clk_enable(edp_drv->pixel_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable pixel clk\n", __func__); |
| goto c3; |
| } |
| ret = clk_enable(edp_drv->ahb_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable ahb clk\n", __func__); |
| goto c2; |
| } |
| ret = clk_enable(edp_drv->link_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable link clk\n", __func__); |
| goto c1; |
| } |
| ret = clk_enable(edp_drv->mdp_core_clk); |
| if (ret) { |
| pr_err("%s: Failed to enable mdp_core_clk\n", __func__); |
| goto c0; |
| } |
| |
| edp_drv->clk_on = 1; |
| |
| return 0; |
| |
| c0: |
| clk_disable(edp_drv->link_clk); |
| c1: |
| clk_disable(edp_drv->ahb_clk); |
| c2: |
| clk_disable(edp_drv->pixel_clk); |
| c3: |
| clk_disable(edp_drv->aux_clk); |
| c4: |
| return ret; |
| } |
| |
| void mdss_edp_clk_disable(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| if (edp_drv->clk_on == 0) { |
| pr_info("%s: edp clks are already OFF\n", __func__); |
| return; |
| } |
| |
| clk_disable(edp_drv->aux_clk); |
| clk_disable(edp_drv->pixel_clk); |
| clk_disable(edp_drv->ahb_clk); |
| clk_disable(edp_drv->link_clk); |
| clk_disable(edp_drv->mdp_core_clk); |
| |
| edp_drv->clk_on = 0; |
| } |
| |
| int mdss_edp_prepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| int ret; |
| |
| /* ahb clock should be prepared first */ |
| ret = clk_prepare(edp_drv->ahb_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare ahb clk\n", __func__); |
| goto c3; |
| } |
| ret = clk_prepare(edp_drv->aux_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare aux clk\n", __func__); |
| goto c2; |
| } |
| |
| /* need mdss clock to receive irq */ |
| ret = clk_prepare(edp_drv->mdp_core_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare mdp_core clk\n", __func__); |
| goto c1; |
| } |
| |
| return 0; |
| c1: |
| clk_unprepare(edp_drv->aux_clk); |
| c2: |
| clk_unprepare(edp_drv->ahb_clk); |
| c3: |
| return ret; |
| |
| } |
| |
| void mdss_edp_unprepare_aux_clocks(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| clk_unprepare(edp_drv->mdp_core_clk); |
| clk_unprepare(edp_drv->aux_clk); |
| clk_unprepare(edp_drv->ahb_clk); |
| } |
| |
| int mdss_edp_prepare_clocks(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| int ret; |
| |
| mdss_edp_clk_set_rate(edp_drv); |
| |
| /* ahb clock should be prepared first */ |
| ret = clk_prepare(edp_drv->ahb_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare ahb clk\n", __func__); |
| goto c4; |
| } |
| ret = clk_prepare(edp_drv->aux_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare aux clk\n", __func__); |
| goto c3; |
| } |
| ret = clk_prepare(edp_drv->pixel_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare pixel clk\n", __func__); |
| goto c2; |
| } |
| ret = clk_prepare(edp_drv->link_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare link clk\n", __func__); |
| goto c1; |
| } |
| ret = clk_prepare(edp_drv->mdp_core_clk); |
| if (ret) { |
| pr_err("%s: Failed to prepare mdp_core clk\n", __func__); |
| goto c0; |
| } |
| |
| return 0; |
| c0: |
| clk_unprepare(edp_drv->link_clk); |
| c1: |
| clk_unprepare(edp_drv->pixel_clk); |
| c2: |
| clk_unprepare(edp_drv->aux_clk); |
| c3: |
| clk_unprepare(edp_drv->ahb_clk); |
| c4: |
| return ret; |
| } |
| |
| void mdss_edp_unprepare_clocks(struct mdss_edp_drv_pdata *edp_drv) |
| { |
| clk_unprepare(edp_drv->mdp_core_clk); |
| clk_unprepare(edp_drv->aux_clk); |
| clk_unprepare(edp_drv->pixel_clk); |
| clk_unprepare(edp_drv->link_clk); |
| /* ahb clock should be last one to disable */ |
| clk_unprepare(edp_drv->ahb_clk); |
| } |
| |
| void mdss_edp_clk_debug(unsigned char *edp_base, unsigned char *mmss_cc_base) |
| { |
| u32 da4, da0, d32c; |
| u32 dc4, dc0, d330; |
| |
| /* pixel clk */ |
| da0 = edp_read(mmss_cc_base + 0x0a0); |
| da4 = edp_read(mmss_cc_base + 0x0a4); |
| d32c = edp_read(mmss_cc_base + 0x32c); |
| |
| /* main link clk */ |
| dc0 = edp_read(mmss_cc_base + 0x0c0); |
| dc4 = edp_read(mmss_cc_base + 0x0c4); |
| d330 = edp_read(mmss_cc_base + 0x330); |
| |
| pr_err("%s: da0=%x da4=%x d32c=%x dc0=%x dc4=%x d330=%x\n", __func__, |
| (int)da0, (int)da4, (int)d32c, (int)dc0, (int)dc4, (int)d330); |
| |
| } |