From 22eb250ea414cc79fc4cc96250553b0f08f2765a Mon Sep 17 00:00:00 2001 From: Sandor Yu <Sandor.yu@nxp.com> Date: Fri, 29 Sep 2017 14:22:36 +0800 Subject: [PATCH] MLK-16538-3: hdmi/dp: Add imx8qm hdmi/dp driver Add hdmi/dp drm architecture driver. HDMI and DP driver can work in imx8qm ARM2 board. The driver support basic hotplug function. Default working mode is 1080p60. Acked-by: Robby Cai <robby.cai@nxp.com> Signed-off-by: Sandor Yu <Sandor.yu@nxp.com> Signed-off-by: Oliver Brown <oliver.brown@nxp.com> (Vipul: Fixed merge conflicts) Signed-off-by: Vipul Kumar <vipul_kumar@mentor.com> Signed-off-by: Srikanth Krishnakar <Srikanth_Krishnakar@mentor.com> --- drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.c | 393 +++++++ drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.h | 165 +++ .../imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.c | 592 ++++++++++ .../imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.h | 62 + drivers/gpu/drm/imx/hdp/Kconfig | 11 + drivers/gpu/drm/imx/hdp/Makefile | 5 + drivers/gpu/drm/imx/hdp/hdmitx_firmware.h | 77 ++ drivers/gpu/drm/imx/hdp/imx-dp.c | 196 ++++ drivers/gpu/drm/imx/hdp/imx-dp.h | 24 + drivers/gpu/drm/imx/hdp/imx-hdmi.c | 148 +++ drivers/gpu/drm/imx/hdp/imx-hdmi.h | 23 + drivers/gpu/drm/imx/hdp/imx-hdp.c | 1022 +++++++++++++++++ drivers/gpu/drm/imx/hdp/imx-hdp.h | 193 ++++ drivers/gpu/drm/imx/hdp/mhdp_firmware.h | 77 ++ .../gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.c | 106 ++ .../gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.h | 109 ++ 16 files changed, 3203 insertions(+) create mode 100644 drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.c create mode 100644 drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.h create mode 100644 drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.c create mode 100644 drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.h create mode 100644 drivers/gpu/drm/imx/hdp/Kconfig create mode 100644 drivers/gpu/drm/imx/hdp/Makefile create mode 100644 drivers/gpu/drm/imx/hdp/hdmitx_firmware.h create mode 100644 drivers/gpu/drm/imx/hdp/imx-dp.c create mode 100644 drivers/gpu/drm/imx/hdp/imx-dp.h create mode 100644 drivers/gpu/drm/imx/hdp/imx-hdmi.c create mode 100644 drivers/gpu/drm/imx/hdp/imx-hdmi.h create mode 100644 drivers/gpu/drm/imx/hdp/imx-hdp.c create mode 100644 drivers/gpu/drm/imx/hdp/imx-hdp.h create mode 100644 drivers/gpu/drm/imx/hdp/mhdp_firmware.h create mode 100644 drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.c create mode 100644 drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.h diff --git a/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.c b/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.c new file mode 100644 index 00000000000000..799162fdff773f --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.c @@ -0,0 +1,393 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Copyright 2017 NXP + * + ****************************************************************************** + * + * API_AFE_mcu1_dp.c + * + ****************************************************************************** + */ + +#include <linux/delay.h> +#include "API_AFE_mcu1_dp.h" +#include "../../../../mxc/hdp/all.h" + +static void AFE_WriteReg(state_struct *state, ENUM_AFE_LINK_RATE link_rate, + u32 addr, u32 val1_6, u32 val2_7, u32 val5_4) +{ + switch (link_rate) { + case AFE_LINK_RATE_1_6: + Afe_write(state, addr, val1_6); + break; + + case AFE_LINK_RATE_2_7: + Afe_write(state, addr, val2_7); + break; + + case AFE_LINK_RATE_5_4: + Afe_write(state, addr, val5_4); + break; + + case AFE_LINK_RATE_8_1: /* Not used in MCU1 */ + default: + pr_err("Warning. Unsupported Link Rate!\n"); + break; + } +} + +void phy_cfg_24mhz(state_struct *state, int num_lanes) +{ + int k; + + for (k = 0; k < num_lanes; k++) { + Afe_write(state, XCVR_DIAG_LANE_FCM_EN_MGN_TMR | (k << 9), 0x0090); + Afe_write(state, TX_RCVDET_EN_TMR | (k << 9), 0x0960); + Afe_write(state, TX_RCVDET_ST_TMR | (k << 9), 0x0030); + } +} + +/* Valid for 24 MHz only */ +void phy_cfg_dp_cmn_pll0(state_struct *state, int num_lanes) +{ + int k; + volatile u16 rdata; + + Afe_write(state, PHY_HDP_CLK_CTL, 0x2405); + Afe_write(state, CMN_PLL0_INTDIV, 0x0086); + Afe_write(state, CMN_PLL0_FRACDIV, 0xF915); + Afe_write(state, CMN_PLL0_HIGH_THR, 0x0022); + Afe_write(state, CMN_PLL0_SS_CTRL1, 0x0140); + rdata = Afe_read(state, CMN_DIAG_HSCLK_SEL); + rdata = rdata & 0xFFFC; + rdata = rdata | 0x0001; + Afe_write(state, CMN_DIAG_HSCLK_SEL, rdata); + for (k = 0; k < num_lanes; k = k + 1) { + rdata = Afe_read(state, (XCVR_DIAG_HSCLK_SEL | (k << 9))); + rdata = rdata & 0xCFFF; + rdata = rdata | 0x1000; + Afe_write(state, (XCVR_DIAG_HSCLK_SEL | (k << 9)), rdata); + } + Afe_write(state, CMN_PLL0_VCOCAL_START, 0x3061); + Afe_write(state, CMN_PLLSM0_USER_DEF_CTRL, 0x0000); + Afe_write(state, CMN_DIAG_PLL0_V2I_TUNE, 0x0006); + Afe_write(state, CMN_DIAG_PLL0_PTATIS_TUNE1, 0x008c); + Afe_write(state, CMN_DIAG_PLL0_PTATIS_TUNE2, 0x002e); + Afe_write(state, CMN_DIAG_PLL0_CP_TUNE, 0x0026); + Afe_write(state, CMN_DIAG_PLL0_LF_PROG, 0x0008); + Afe_write(state, CMN_PLL0_VCOCAL_INIT_TMR, 0x00F0); + Afe_write(state, CMN_PLL0_VCOCAL_ITER_TMR, 0x0018); + Afe_write(state, CMN_PLL0_SS_CTRL2, 0x7F03); + Afe_write(state, CMN_PLL0_DSM_DIAG, 0x0020); + Afe_write(state, CMN_DIAG_PLL0_OVRD, 0x0000); + Afe_write(state, CMN_DIAG_PLL0_FBH_OVRD, 0x0000); + Afe_write(state, CMN_DIAG_PLL0_FBL_OVRD, 0x0000); +} + +/* Valid for 24 MHz only */ +void phy_cfg_dp_pll0(state_struct *state, int num_lanes, ENUM_AFE_LINK_RATE link_rate) +{ + int k; + volatile u16 rdata; + + rdata = Afe_read(state, PHY_HDP_CLK_CTL); + rdata = rdata & 0x00FF; + + switch (link_rate) { + case AFE_LINK_RATE_1_6: + case AFE_LINK_RATE_2_7: + rdata = rdata | 0x2400; + break; + + case AFE_LINK_RATE_5_4: + rdata = rdata | 0x1200; + break; + + case AFE_LINK_RATE_8_1: /* nNot used in MCU1 */ + default: + pr_err("Warning. Unsupported Link Rate!\n"); + break; + } + + Afe_write(state, PHY_HDP_CLK_CTL, rdata); + rdata = Afe_read(state, CMN_DIAG_HSCLK_SEL); + rdata = rdata & 0xFFCC; + switch (link_rate) { + case AFE_LINK_RATE_1_6: + case AFE_LINK_RATE_2_7: + rdata = rdata | 0x0011; + break; + + case AFE_LINK_RATE_5_4: + rdata = rdata | 0x0000; + break; + + case AFE_LINK_RATE_8_1: /* Not used in MCU1 */ + default: + pr_err("Warning. Unsupported Link Rate!\n"); + break; + } + Afe_write(state, CMN_DIAG_HSCLK_SEL, rdata); + for (k = 0; k < num_lanes; k = k + 1) { + rdata = Afe_read(state, (XCVR_DIAG_HSCLK_SEL | (k << 9))); + rdata = rdata & 0xCFFF; + switch (link_rate) { + case AFE_LINK_RATE_1_6: + case AFE_LINK_RATE_2_7: + rdata = rdata | 0x1000; + break; + + case AFE_LINK_RATE_5_4: + rdata = rdata | 0x0000; + break; + + case AFE_LINK_RATE_8_1: /* Not used in MCU1 */ + default: + pr_err("Warning. Unsupported Link Rate!\n"); + break; + } + Afe_write(state, (XCVR_DIAG_HSCLK_SEL | (k << 9)), rdata); + } + AFE_WriteReg(state, link_rate, CMN_PLL0_VCOCAL_START, 0x3061, 0x30D0, 0x30D0); + AFE_WriteReg(state, link_rate, CMN_PLLSM0_USER_DEF_CTRL, 0x0000, 0x1000, + 0x1000); + AFE_WriteReg(state, link_rate, CMN_DIAG_PLL0_V2I_TUNE, 0x0006, 0x0007, 0x0007); + Afe_write(state, CMN_DIAG_PLL0_PTATIS_TUNE1, 0x008C); + Afe_write(state, CMN_DIAG_PLL0_PTATIS_TUNE2, 0x002E); + AFE_WriteReg(state, link_rate, CMN_DIAG_PLL0_CP_TUNE, 0x0026, 0x0029, 0x0029); + Afe_write(state, CMN_DIAG_PLL0_LF_PROG, 0x0008); + Afe_write(state, CMN_PLL0_VCOCAL_INIT_TMR, 0x00F0); + Afe_write(state, CMN_PLL0_VCOCAL_ITER_TMR, 0x0018); + AFE_WriteReg(state, link_rate, CMN_PLL0_INTDIV, 0x0086, 0x00E0, 0x00E0); + AFE_WriteReg(state, link_rate, CMN_PLL0_FRACDIV, 0xF915, 0xF479, 0xF479); + AFE_WriteReg(state, link_rate, CMN_PLL0_HIGH_THR, 0x0022, 0x0038, 0x0038); + AFE_WriteReg(state, link_rate, CMN_PLL0_SS_CTRL1, 0x0140, 0x0204, 0x0204); + Afe_write(state, CMN_PLL0_SS_CTRL2, 0x7F03); + Afe_write(state, CMN_PLL0_DSM_DIAG, 0x0020); + Afe_write(state, CMN_DIAG_PLL0_OVRD, 0x0000); + Afe_write(state, CMN_DIAG_PLL0_FBH_OVRD, 0x0000); + Afe_write(state, CMN_DIAG_PLL0_FBL_OVRD, 0x0000); + for (k = 0; k < num_lanes; k = k + 1) { + rdata = Afe_read(state, (XCVR_DIAG_PLLDRC_CTRL | (k << 9))); + rdata = rdata & 0x8FFF; + switch (link_rate) { + case AFE_LINK_RATE_1_6: + case AFE_LINK_RATE_2_7: + rdata = rdata | 0x2000; + break; + + case AFE_LINK_RATE_5_4: + rdata = rdata | 0x1000; + break; + + case AFE_LINK_RATE_8_1: /* Not used in MCU1 */ + default: + pr_err("Warning. Unsupported Link Rate!\n"); + break; + } + Afe_write(state, (XCVR_DIAG_PLLDRC_CTRL | (k << 9)), rdata); + } +} + +void phy_cfg_dp_ln(state_struct *state, int num_lanes) +{ + int k; + for (k = 0; k < num_lanes; k = k + 1) { + Afe_write(state, (XCVR_PSM_RCTRL | (k << 9)), 0xBEFC); + Afe_write(state, (TX_PSC_A0 | (k << 9)), 0x6799); + Afe_write(state, (TX_PSC_A1 | (k << 9)), 0x6798); + Afe_write(state, (TX_PSC_A2 | (k << 9)), 0x0098); + Afe_write(state, (TX_PSC_A3 | (k << 9)), 0x0098); + } +} + +u16 aux_cal_cfg(state_struct *state, u16 prev_calib_code) +{ + u16 txpu_calib_code; + u16 txpd_calib_code; + u16 txpu_adj_calib_code; + u16 txpd_adj_calib_code; + u16 new_calib_code; + u16 rdata; + + txpu_calib_code = Afe_read(state, CMN_TXPUCAL_CTRL); + txpd_calib_code = Afe_read(state, CMN_TXPDCAL_CTRL); + txpu_adj_calib_code = Afe_read(state, CMN_TXPU_ADJ_CTRL); + txpd_adj_calib_code = Afe_read(state, CMN_TXPD_ADJ_CTRL); + + new_calib_code = ((txpu_calib_code + txpd_calib_code) / 2) + + txpu_adj_calib_code + txpd_adj_calib_code; + + if (new_calib_code != prev_calib_code) { + rdata = Afe_read(state, TX_ANA_CTRL_REG_1); + rdata &= 0xDFFF; + Afe_write(state, TX_ANA_CTRL_REG_1, rdata); + Afe_write(state, TX_DIG_CTRL_REG_2, new_calib_code); + udelay(10000); + rdata |= 0x2000; + Afe_write(state, TX_ANA_CTRL_REG_1, rdata); + udelay(150); + } + + return new_calib_code; +} + +void aux_cfg(state_struct *state) +{ + volatile u16 rdata; + + Afe_write(state, TX_DIG_CTRL_REG_2, 36); + + Afe_write(state, TX_ANA_CTRL_REG_2, 0x0100); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_2, 0x0300); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_3, 0x0000); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_1, 0x2008); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_1, 0x2018); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_1, 0xA018); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_2, 0x030C); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_5, 0x0000); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_4, 0x1001); + udelay(150); + Afe_write(state, TX_ANA_CTRL_REG_1, 0xA098); + udelay(5000); + Afe_write(state, TX_ANA_CTRL_REG_1, 0xA198); + udelay(5000); + Afe_write(state, TX_ANA_CTRL_REG_2, 0x030D); + udelay(5000); + Afe_write(state, TX_ANA_CTRL_REG_2, 0x030F); + udelay(5000); + + pr_info("TX_ANA_CTRL_REG_1 %x)\n", rdata); + rdata = Afe_read(state, TX_ANA_CTRL_REG_2); + pr_info("TX_ANA_CTRL_REG_2 %x)\n", rdata); + rdata = Afe_read(state, TX_ANA_CTRL_REG_3); + pr_info("TX_ANA_CTRL_REG_3 %x)\n", rdata); + rdata = Afe_read(state, TX_ANA_CTRL_REG_4); + pr_info("TX_ANA_CTRL_REG_4 %x)\n", rdata); + rdata = Afe_read(state, TX_ANA_CTRL_REG_5); + pr_info("TX_ANA_CTRL_REG_5 %x)\n", rdata); +} + +void AFE_init(state_struct *state, int num_lanes, ENUM_AFE_LINK_RATE link_rate) +{ + volatile u16 val; + + val = Afe_read(state, PHY_PMA_CMN_CTRL1); + val = val & 0xFFF7; + val = val | 0x0008; + Afe_write(state, PHY_PMA_CMN_CTRL1, val); + + Afe_write(state, CMN_DIAG_PLL0_TEST_MODE, 0x0020); + Afe_write(state, CMN_PSM_CLK_CTRL, 0x0016); + + phy_cfg_24mhz(state, num_lanes); + + phy_cfg_dp_pll0(state, num_lanes, link_rate); + + val = Afe_read(state, PHY_PMA_CMN_CTRL1); + val = val & 0xFF8F; + val = val | 0x0030; + Afe_write(state, PHY_PMA_CMN_CTRL1, val); + + phy_cfg_dp_ln(state, num_lanes); + + /* Configure PHY in A2 Mode */ + Afe_write(state, PHY_HDP_MODE_CTRL, 0x0004); + +} + +void AFE_power(state_struct *state, int num_lanes, ENUM_AFE_LINK_RATE link_rate) +{ + static u16 prev_calib_code; + + volatile u16 val; + + Afe_write(state, TX_DIAG_ACYA_0, 1); + Afe_write(state, TX_DIAG_ACYA_1, 1); + Afe_write(state, TX_DIAG_ACYA_2, 1); + Afe_write(state, TX_DIAG_ACYA_3, 1); + + Afe_write(state, TXDA_CYA_AUXDA_CYA, 1); + + /* Wait for A2 ACK (PHY_HDP_MODE_CTL [6] = 1’b1) */ + do { + val = Afe_read(state, PHY_HDP_MODE_CTRL); + val = val >> 6; + } while ((val & 1) == 0); + + /* to check if PLL has locked (bit 6) */ + val = Afe_read(state, PHY_PMA_CMN_CTRL2); + val = val >> 6; + + if ((val & 1) == 0) { + pr_err("ERROR: PLL is not locked\n"); + } else { + pr_info("PHY_PMA_CMN_CTRL2 = %x\n", val); + } + + /* to check if cmn_ready is asserted (bit 0) */ + val = Afe_read(state, PHY_PMA_CMN_CTRL1); + + if ((val & 1) == 0) { + pr_err("ERROR: cmn_ready is not asserted\n"); + } else { + pr_info("PHY_PMA_CMN_CTRL1 = %x\n", val); + } + + /* Configure PHY in A0 mode (PHY must be in the A0 power state in order to transmit data) */ + Afe_write(state, PHY_HDP_MODE_CTRL, 0x0101); + + /* Wait for A2 ACK (PHY_HDP_MODE_CTL [4] = 1’b1) */ + do { + val = Afe_read(state, PHY_HDP_MODE_CTRL); + val = val >> 4; + } while ((val & 1) == 0); + + prev_calib_code = aux_cal_cfg(state, prev_calib_code); + + aux_cfg(state); + +} diff --git a/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.h b/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.h new file mode 100644 index 00000000000000..2b4954878e275b --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/API_AFE_mcu1_dp.h @@ -0,0 +1,165 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * API_AFE_mcu1_dp.h + * + ****************************************************************************** + */ + +#ifndef API_AFE_MCU1_DP_H +# define API_AFE_MCU1_DP_H + +#define PHY_PMA_CMN_CTRL1 0xC800 +#define PHY_PMA_CMN_CTRL2 0xC801 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40F2 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_0 0x40F2 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_1 0x42F2 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_2 0x44F2 +#define XCVR_DIAG_LANE_FCM_EN_MGN_TMR_3 0x46F2 +#define TX_RCVDET_EN_TMR 0x4122 +#define TX_RCVDET_EN_TMR_0 0x4122 +#define TX_RCVDET_EN_TMR_1 0x4322 +#define TX_RCVDET_EN_TMR_2 0x4522 +#define TX_RCVDET_EN_TMR_3 0x4722 +#define TX_RCVDET_ST_TMR 0x4123 +#define TX_RCVDET_ST_TMR_0 0x4123 +#define TX_RCVDET_ST_TMR_1 0x4323 +#define TX_RCVDET_ST_TMR_2 0x4523 +#define TX_RCVDET_ST_TMR_3 0x4723 +#define PHY_HDP_CLK_CTL 0xC009 +#define CMN_DIAG_HSCLK_SEL 0x01E0 +#define XCVR_DIAG_HSCLK_SEL 0x40E1 +#define XCVR_DIAG_HSCLK_SEL_0 0x40E1 +#define XCVR_DIAG_HSCLK_SEL_1 0x42E1 +#define XCVR_DIAG_HSCLK_SEL_2 0x44E1 +#define XCVR_DIAG_HSCLK_SEL_3 0x46E1 +#define CMN_PLL0_VCOCAL_START 0x0081 +#define CMN_PLLSM0_USER_DEF_CTRL 0x002F +#define CMN_DIAG_PLL0_V2I_TUNE 0x01C5 +#define CMN_DIAG_PLL0_PTATIS_TUNE1 0x01C8 +#define CMN_DIAG_PLL0_PTATIS_TUNE2 0x01C9 +#define CMN_DIAG_PLL0_CP_TUNE 0x01C6 +#define CMN_DIAG_PER_CAL_ADJ 0x01EC +#define CMN_DIAG_PLL0_LF_PROG 0x01C7 +#define CMN_PLL0_VCOCAL_INIT_TMR 0x0084 +#define CMN_PLL0_VCOCAL_ITER_TMR 0x0085 +#define CMN_PLL0_INTDIV 0x0094 +#define CMN_PLL0_FRACDIV 0x0095 +#define CMN_PLL0_HIGH_THR 0x0096 +#define CMN_PLL0_SS_CTRL1 0x0098 +#define CMN_PLL0_SS_CTRL2 0x0099 +#define CMN_PLL0_DSM_DIAG 0x0097 +#define CMN_DIAG_PLL0_OVRD 0x01C2 +#define CMN_DIAG_PLL0_FBH_OVRD 0x01C0 +#define CMN_DIAG_PLL0_FBL_OVRD 0x01C1 +#define XCVR_DIAG_PLLDRC_CTRL 0x40E0 +#define XCVR_DIAG_PLLDRC_CTRL_0 0x40E0 +#define XCVR_DIAG_PLLDRC_CTRL_1 0x42E0 +#define XCVR_DIAG_PLLDRC_CTRL_2 0x44E0 +#define XCVR_DIAG_PLLDRC_CTRL_3 0x46E0 +#define CMN_DIAG_PLL0_TEST_MODE 0x01C4 +#define PHY_HDP_MODE_CTRL 0xC008 +#define XCVR_PSM_RCTRL 0x4001 +#define XCVR_PSM_RCTRL_0 0x4001 +#define XCVR_PSM_RCTRL_1 0x4201 +#define XCVR_PSM_RCTRL_2 0x4401 +#define XCVR_PSM_RCTRL_3 0x4601 +#define TX_PSC_A0 0x4100 +#define TX_PSC_A0_0 0x4100 +#define TX_PSC_A0_1 0x4300 +#define TX_PSC_A0_2 0x4500 +#define TX_PSC_A0_3 0x4700 +#define TX_PSC_A1 0x4101 +#define TX_PSC_A1_0 0x4101 +#define TX_PSC_A1_1 0x4301 +#define TX_PSC_A1_2 0x4501 +#define TX_PSC_A1_3 0x4701 +#define TX_PSC_A2 0x4102 +#define TX_PSC_A2_0 0x4102 +#define TX_PSC_A2_1 0x4302 +#define TX_PSC_A2_2 0x4502 +#define TX_PSC_A2_3 0x4702 +#define TX_PSC_A3 0x4103 +#define TX_PSC_A3_0 0x4103 +#define TX_PSC_A3_1 0x4303 +#define TX_PSC_A3_2 0x4503 +#define TX_PSC_A3_3 0x4703 +#define TX_DIAG_TX_DRV 0x41E1 +#define TX_DIAG_TX_DRV_0 0x41E1 +#define TX_DIAG_TX_DRV_1 0x43E1 +#define TX_DIAG_TX_DRV_2 0x45E1 +#define TX_DIAG_TX_DRV_3 0x47E1 +#define PHY_HDP_MODE CTRL 0xC008 +#define TX_DIAG_ACYA_0 0x41ff +#define TX_DIAG_ACYA_1 0x43ff +#define TX_DIAG_ACYA_2 0x45ff +#define TX_DIAG_ACYA_3 0x47ff + +#define TX_TXCC_MGNFS_MULT_000_0 0x4050 +#define TX_TXCC_MGNFS_MULT_000_1 0x4250 +#define TX_TXCC_MGNFS_MULT_000_2 0x4450 +#define TX_TXCC_MGNFS_MULT_000_3 0x4650 + +#define TX_TXCC_CPOST_MULT_00_0 0x404C +#define TX_TXCC_CPOST_MULT_00_1 0x424C +#define TX_TXCC_CPOST_MULT_00_2 0x444C +#define TX_TXCC_CPOST_MULT_00_3 0x464C + +#define TX_ANA_CTRL_REG_1 0x5020 +#define TX_ANA_CTRL_REG_2 0x5021 +#define TX_ANA_CTRL_REG_3 0x5026 +#define TX_ANA_CTRL_REG_4 0x5027 +#define TX_ANA_CTRL_REG_5 0x5029 + +#define TX_DIG_CTRL_REG_2 0x5024 +#define TX_DIG_CTRL_REG_1 0x5023 +#define TXDA_CYA_AUXDA_CYA 0x5025 +#define CMN_TXPUCAL_CTRL 0x00E0 +#define CMN_TXPDCAL_CTRL 0x00F0 +#define CMN_TXPU_ADJ_CTRL 0x0108 +#define CMN_TXPD_ADJ_CTRL 0x010c +#define TXDA_COEFF_CALC 0x5022 +#define CMN_PSM_CLK_CTRL 0x0061 + +#define PHY_HDP_TX_CTL_L0 0xC408 +#define PHY_HDP_TX_CTL_L1 0xC448 +#define PHY_HDP_TX_CTL_L2 0xC488 +#define PHY_HDP_TX_CTL_L3 0xC4C8 + +#endif //API_AFE_MCU1_DP_H diff --git a/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.c b/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.c new file mode 100644 index 00000000000000..861cc63635dc2c --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.c @@ -0,0 +1,592 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Copyright 2017 NXP + * + ****************************************************************************** + * + * API_AFE_ss28fdsoi_kiran_hdmitx.c + * + ****************************************************************************** + */ + +#include <linux/io.h> +#include "API_AFE_ss28fdsoi_kiran_hdmitx.h" +#include "ss28fdsoi_hdmitx_table.h" + +static int inside(u32 value, u32 left_sharp_corner, + u32 right_sharp_corner) +{ + if (value < left_sharp_corner) + return 0; + if (value > right_sharp_corner) + return 0; + return 1; +} + +int get_table_row_match_column(const u32 *array, u32 table_rows, + u32 table_cols, u32 start_row, + u32 column_to_search, + u32 value_to_search_in_column) +{ + u32 idx_cols, idx_rows; + u32 value; + for (idx_rows = start_row; idx_rows < table_rows; idx_rows++) { + for (idx_cols = 0; idx_cols < table_cols; idx_cols++) { + /* pr_info("get_table_row_match_column: TAG row: %0d, col: %0d, value: %0d\n", + idx_rows, idx_cols, *((array + idx_rows * table_cols) + idx_cols)); */ + if (idx_cols == column_to_search) { + value = + *((array + idx_rows * table_cols) + + idx_cols); + if (value == value_to_search_in_column) { + return idx_rows; + } + } + } + } + return -1; +} + +int get_table_row(const u32 *array, u32 table_rows, + u32 table_cols, u32 variable_in_range, + u32 range_min_column, u32 range_max_column, + u32 column_to_search, u32 column_value) +{ + u32 i = 0; + while (1) { + i = get_table_row_match_column(array, table_rows, table_cols, i, + column_to_search, column_value); + if (i + 1) { + if (inside(variable_in_range, + *((array + i * table_cols) + range_min_column), + *((array + i * table_cols) + range_max_column))) { + break; + } + i++; + } else { + break; + } + } + return i; +} + +int phy_cfg_hdp_ss28fdsoi(state_struct *state, int num_lanes, VIC_MODES vicMode, int bpp, + VIC_PXL_ENCODING_FORMAT format) +{ + const int phy_reset_workaround = 0; + u32 vco_freq_khz; + unsigned char i; + u32 row, feedback_factor; + uint32_t reg_val; + int pixel_freq_khz = vic_table[vicMode][PIXEL_FREQ_KHZ]; + uint32_t character_clock_ratio_num = 1; + uint32_t character_clock_ratio_den = 1; + int character_freq_khz; + u32 ftemp; + clk_ratio_t clk_ratio = CLK_RATIO_1_1; + + reg_field_t cmnda_pll0_hs_sym_div_sel; + reg_field_t cmnda_pll0_ip_div; + reg_field_t cmnda_pll0_fb_div_low; + reg_field_t cmnda_pll0_fb_div_high; + reg_field_t cmn_ref_clk_dig_div; + reg_field_t divider_scaler; + reg_field_t cmnda_hs_clk_0_sel; + reg_field_t cmnda_hs_clk_1_sel; + reg_field_t tx_subrate; + reg_field_t vco_ring_select; + reg_field_t pll_feedback_divider_total; + reg_field_t voltage_to_current_coarse; + reg_field_t voltage_to_current; + reg_field_t ndac_ctrl; + reg_field_t pmos_ctrl; + reg_field_t ptat_ndac_ctrl; + reg_field_t charge_pump_gain; + + /* Set field position in a target register */ + cmnda_pll0_fb_div_high.value = 0x00A; + cmnda_pll0_hs_sym_div_sel.msb = 9; + cmnda_pll0_hs_sym_div_sel.lsb = 8; + cmnda_pll0_ip_div.msb = 7; + cmnda_pll0_ip_div.lsb = 0; + cmnda_pll0_fb_div_low.msb = 9; + cmnda_pll0_fb_div_low.lsb = 0; + cmnda_pll0_fb_div_high.msb = 9; + cmnda_pll0_fb_div_high.lsb = 0; + cmn_ref_clk_dig_div.msb = 13; + cmn_ref_clk_dig_div.lsb = 12; + divider_scaler.msb = 14; + divider_scaler.lsb = 12; + cmnda_hs_clk_0_sel.msb = 1; + cmnda_hs_clk_0_sel.lsb = 0; + cmnda_hs_clk_1_sel.msb = 1; + cmnda_hs_clk_1_sel.lsb = 0; + tx_subrate.msb = 2; + tx_subrate.lsb = 0; + vco_ring_select.msb = 12; + vco_ring_select.lsb = 12; + pll_feedback_divider_total.msb = 9; + pll_feedback_divider_total.lsb = 0; + voltage_to_current_coarse.msb = 2; + voltage_to_current_coarse.lsb = 0; + voltage_to_current.msb = 5; + voltage_to_current.lsb = 4; + ndac_ctrl.msb = 11; + ndac_ctrl.lsb = 8; + pmos_ctrl.msb = 7; + pmos_ctrl.lsb = 0; + ptat_ndac_ctrl.msb = 5; + ptat_ndac_ctrl.lsb = 0; + charge_pump_gain.msb = 8; + charge_pump_gain.lsb = 0; + + pr_info + ("phy_cfg_hdp() num_lanes: %0d, vicMode: %0d, color depth: %0d-bit, encoding: %0d\n", + num_lanes, vicMode, bpp, format); + + /* register PHY_PMA_ISOLATION_CTRL + enable PHY isolation mode only for CMN */ + if (phy_reset_workaround) { + Afe_write(state, 0xC81F, 0xD000); + reg_val = Afe_read(state, 0xC812); + reg_val &= 0xFF00; + reg_val |= 0x0012; + /* set cmn_pll0_clk_datart1_div/cmn_pll0_clk_datart0_div dividers */ + Afe_write(state, 0xC812, reg_val); + /* register PHY_ISO_CMN_CTRL */ + Afe_write(state, 0xC010, 0x0000); /* assert PHY reset from isolation register */ + /* register PHY_PMA_ISO_CMN_CTRL */ + Afe_write(state, 0xC810, 0x0000); /* assert PMA CMN reset */ + /* register XCVR_DIAG_BIDI_CTRL */ + for (i = 0; i < num_lanes; i++) { + Afe_write(state, 0x40E8 | (i << 9), 0x00FF); + } + } else { + /*-------------------------------------------------------------- + * Describing Task phy_cfg_hdp + * ------------------------------------------------------------*/ + /* register PHY_PMA_CMN_CTRL1 */ + for (i = 0; i < num_lanes; i++) + Afe_write(state, 0x40E8 | (i << 9), 0x007F); + } + /* register CMN_DIAG_PLL0_TEST_MODE */ + Afe_write(state, 0x01C4, 0x0020); + /* register CMN_PSM_CLK_CTRL */ + Afe_write(state, 0x0061, 0x0016); + + /* Determine the TMDS/PIXEL clock ratio */ + switch (format) { + case YCBCR_4_2_2: + clk_ratio = CLK_RATIO_1_1; + character_clock_ratio_num = 1; + character_clock_ratio_den = 1; + break; + case YCBCR_4_2_0: + switch (bpp) { + case 8: + clk_ratio = CLK_RATIO_1_2; + character_clock_ratio_num = 1; + character_clock_ratio_den = 2; + break; + case 10: + clk_ratio = CLK_RATIO_5_8; + character_clock_ratio_num = 5; + character_clock_ratio_den = 8; + break; + case 12: + clk_ratio = CLK_RATIO_3_4; + character_clock_ratio_num = 3; + character_clock_ratio_den = 4; + break; + case 16: + clk_ratio = CLK_RATIO_1_1; + character_clock_ratio_num = 1; + character_clock_ratio_den = 1; + break; + default: + pr_err("Invalid ColorDepth\n"); + } + break; + + default: + switch (bpp) { /* Assume RGB */ + case 10: + clk_ratio = CLK_RATIO_5_4; + character_clock_ratio_num = 5; + character_clock_ratio_den = 4; + break; + case 12: + clk_ratio = CLK_RATIO_3_2; + character_clock_ratio_num = 3; + character_clock_ratio_den = 2; + break; + case 16: + clk_ratio = CLK_RATIO_2_1; + character_clock_ratio_num = 2; + character_clock_ratio_den = 1; + break; + default: + clk_ratio = CLK_RATIO_1_1; + character_clock_ratio_num = 1; + character_clock_ratio_den = 1; + } + } + + /* Determine a relevant feedback factor as used + * in the ss28fdsoi_hdmitx_clock_control_table table */ + switch (clk_ratio) { + case CLK_RATIO_1_1: + feedback_factor = 1000; + break; + case CLK_RATIO_5_4: + feedback_factor = 1250; + break; + case CLK_RATIO_3_2: + feedback_factor = 1500; + break; + case CLK_RATIO_2_1: + feedback_factor = 2000; + break; + case CLK_RATIO_1_2: + feedback_factor = 500; + break; + case CLK_RATIO_5_8: + feedback_factor = 625; + break; + case CLK_RATIO_3_4: + feedback_factor = 750; + break; + } + + /* Get right row from the ss28fdsoi_hdmitx_clock_control_table table. + * Check if 'pixel_freq_khz' falls inside the + * <PIXEL_CLK_FREQ_KHZ_MIN, PIXEL_CLK_FREQ_KHZ_MAX> range. + * Consider only the rows with FEEDBACK_FACTOR column matching feedback_factor. */ + row = + get_table_row((const u32 *)&ss28fdsoi_hdmitx_clock_control_table, + SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_ROWS, + SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_COLS, + pixel_freq_khz, PIXEL_CLK_FREQ_KHZ_MIN, + PIXEL_CLK_FREQ_KHZ_MAX, FEEDBACK_FACTOR, + feedback_factor); + + /* Check if row was found */ + ftemp = pixel_freq_khz; + if (row + 1) { + pr_info + ("Pixel clock frequency (%u kHz) is supported in this color depth (%0d-bit). Settings found in row %0d\n", + ftemp, bpp, row); + } else { + pr_info + ("Pixel clock frequency (%u kHz) not supported for this color depth (%0d-bit), row=%d\n", + ftemp, bpp, row); + } + character_freq_khz = pixel_freq_khz * character_clock_ratio_num / character_clock_ratio_den; + ftemp = character_freq_khz; + pr_info("Character clock frequency: %u kHz.\n", ftemp); + + /* Extract particular values from the ss28fdsoi_hdmitx_clock_control_table table */ + set_field_value(&cmnda_pll0_hs_sym_div_sel, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_PLL0_HS_SYM_DIV_SEL]); + set_field_value(&cmnda_pll0_ip_div, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_PLL0_IP_DIV]); + set_field_value(&cmnda_pll0_fb_div_low, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_PLL0_FB_DIV_LOW]); + set_field_value(&cmnda_pll0_fb_div_high, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_PLL0_FB_DIV_HIGH]); + set_field_value(&cmn_ref_clk_dig_div, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMN_REF_CLK_DIG_DIV]); + set_field_value(÷r_scaler, + ss28fdsoi_hdmitx_clock_control_table[row] + [REF_CLK_DIVIDER_SCALER]); + set_field_value(&cmnda_hs_clk_0_sel, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_HS_CLK_0_SEL]); + set_field_value(&cmnda_hs_clk_1_sel, + ss28fdsoi_hdmitx_clock_control_table[row] + [CMNDA_HS_CLK_1_SEL]); + set_field_value(&tx_subrate, + ss28fdsoi_hdmitx_clock_control_table[row] + [HSCLK_DIV_TX_SUB_RATE]); + set_field_value(&vco_ring_select, + ss28fdsoi_hdmitx_clock_control_table[row] + [VCO_RING_SELECT]); + set_field_value(&pll_feedback_divider_total, + ss28fdsoi_hdmitx_clock_control_table[row] + [PLL_FB_DIV_TOTAL]); + + /* Display parameters (informative message) */ + pr_info("set_field_value() cmnda_pll0_hs_sym_div_sel : 0x%X\n", + cmnda_pll0_hs_sym_div_sel.value); + pr_info("set_field_value() cmnda_pll0_ip_div : 0x%02X\n", + cmnda_pll0_ip_div.value); + pr_info("set_field_value() cmnda_pll0_fb_div_low : 0x%03X\n", + cmnda_pll0_fb_div_low.value); + pr_info("set_field_value() cmnda_pll0_fb_div_high : 0x%03X\n", + cmnda_pll0_fb_div_high.value); + pr_info("set_field_value() cmn_ref_clk_dig_div : 0x%X\n", + cmn_ref_clk_dig_div.value); + pr_info("set_field_value() divider_scaler : 0x%X\n", + divider_scaler.value); + pr_info("set_field_value() cmnda_hs_clk_0_sel : %0d\n", + cmnda_hs_clk_0_sel.value); + pr_info("set_field_value() cmnda_hs_clk_1_sel : %0d\n", + cmnda_hs_clk_1_sel.value); + pr_info("set_field_value() tx_subrate : %0d\n", + tx_subrate.value); + pr_info("set_field_value() vco_ring_select : %0d\n", + vco_ring_select.value); + pr_info("set_field_value() pll_feedback_divider_total: %0d\n", + pll_feedback_divider_total.value); + + vco_freq_khz = + pixel_freq_khz * pll_feedback_divider_total.value / + cmnda_pll0_ip_div.value; + + /* Get right row from the ss28fdsoi_hdmitx_pll_tuning_table table. + * Check if 'vco_freq_mhz' falls inside the + * <PLL_VCO_FREQ_MHZ_MIN, PLL_VCO_FREQ_MHZ_MAX> range. + * Consider only the rows with PLL_FEEDBACK_DIV_TOTAL. + * column matching pll_feedback_divider_total. */ + row = + get_table_row((const u32 *)&ss28fdsoi_hdmitx_pll_tuning_table, + SS28FDSOI_HDMITX_PLL_TUNING_TABLE_ROWS, + SS28FDSOI_HDMITX_PLL_TUNING_TABLE_COLS, vco_freq_khz, + PLL_VCO_FREQ_KHZ_MIN, PLL_VCO_FREQ_KHZ_MAX, + PLL_FEEDBACK_DIV_TOTAL, + pll_feedback_divider_total.value); + ftemp = vco_freq_khz; + if (row + 1) { + pr_info + ("VCO frequency (%u kHz) is supported. Settings found in row %0d\n", + ftemp, row); + } else { + pr_info("VCO frequency (%u kHz) not supported\n", ftemp); + } + + /* Extract particular values from the ss28fdsoi_hdmitx_pll_tuning_table table */ + set_field_value(&voltage_to_current_coarse, + ss28fdsoi_hdmitx_pll_tuning_table[row] + [VOLTAGE_TO_CURRENT_COARSE]); + set_field_value(&voltage_to_current, + ss28fdsoi_hdmitx_pll_tuning_table[row] + [VOLTAGE_TO_CURRENT]); + set_field_value(&ndac_ctrl, + ss28fdsoi_hdmitx_pll_tuning_table[row][NDAC_CTRL]); + set_field_value(&pmos_ctrl, + ss28fdsoi_hdmitx_pll_tuning_table[row][PMOS_CTRL]); + set_field_value(&ptat_ndac_ctrl, + ss28fdsoi_hdmitx_pll_tuning_table[row][PTAT_NDAC_CTRL]); + set_field_value(&charge_pump_gain, + ss28fdsoi_hdmitx_pll_tuning_table[row] + [CHARGE_PUMP_GAIN]); + + /* Display parameters (informative message) */ + pr_info("set_field_value() voltage_to_current_coarse : 0x%X\n", + voltage_to_current_coarse.value); + pr_info("set_field_value() voltage_to_current : 0x%X\n", + voltage_to_current.value); + pr_info("set_field_value() ndac_ctrl : 0x%X\n", + ndac_ctrl.value); + pr_info("set_field_value() pmos_ctrl : 0x%02X\n", + pmos_ctrl.value); + pr_info("set_field_value() ptat_ndac_ctrl : 0x%02X\n", + ptat_ndac_ctrl.value); + pr_info("set_field_value() charge_pump_gain : 0x%03X\n", + charge_pump_gain.value); + + /* --------------------------------------------------------------- + * Describing Task phy_cfg_hdmi_pll0_0pt5736 + *---------------------------------------------------------------*/ + + Afe_write(state, 0x0081, 0x30A0); + /* register CMN_PLL0_VCOCAL_INIT_TMR */ + Afe_write(state, 0x0084, 0x0064); + /* register CMN_PLL0_VCOCAL_ITER_TMR */ + Afe_write(state, 0x0085, 0x000A); + /* register PHY_HDP_CLK_CTL */ + reg_val = Afe_read(state, 0xC009); + reg_val &= 0x00FF; + reg_val |= 0x1200; + Afe_write(state, 0xC009, reg_val); + /* register CMN_DIAG_PLL0_INCLK_CTRL */ + reg_val = set_reg_value(cmnda_pll0_hs_sym_div_sel); + reg_val |= set_reg_value(cmnda_pll0_ip_div); + Afe_write(state, 0x01CA, reg_val); + /* register CMN_DIAG_PLL0_FBL_OVRD */ + reg_val = set_reg_value(cmnda_pll0_fb_div_low); + reg_val |= (1 << 15); + Afe_write(state, 0x01C1, reg_val); + /* register PHY_PMA_CMN_CTRL1 */ + reg_val = Afe_read(state, 0xC800); + reg_val &= 0xCFFF; + reg_val |= set_reg_value(cmn_ref_clk_dig_div); + Afe_write(state, 0xC800, reg_val); + /* register CMN_CDIAG_REFCLK_CTRL */ + reg_val = set_reg_value(divider_scaler); + Afe_write(state, 0x0062, reg_val); + /* register CMN_DIAG_HSCLK_SEL */ + reg_val = Afe_read(state, 0x01E0); + reg_val &= 0xFF00; + reg_val |= (cmnda_hs_clk_0_sel.value >> 1) << 0; + reg_val |= (cmnda_hs_clk_1_sel.value >> 1) << 4; + Afe_write(state, 0x01E0, reg_val); + + /* register XCVR_DIAG_HSCLK_SEL */ + for (i = 0; i < num_lanes; i++) { + reg_val = Afe_read(state, 0x40E1 | (i << 9)); + reg_val &= 0xCFFF; + reg_val |= (cmnda_hs_clk_0_sel.value >> 1) << 12; + Afe_write(state, 0x40E1 | (i << 9), reg_val); + } + + /* register TX_DIAG_TX_CTRL */ + for (i = 0; i < num_lanes; i++) { + reg_val = Afe_read(state, 0x41E0 | (i << 9)); + reg_val &= 0xFF3F; + reg_val |= (tx_subrate.value >> 1) << 6; + Afe_write(state, 0x41E0 | (i << 9), reg_val); + } + + /* register CMN_PLLSM0_USER_DEF_CTRL */ + reg_val = set_reg_value(vco_ring_select); + Afe_write(state, 0x002F, reg_val); + /* register CMN_DIAG_PLL0_OVRD */ + Afe_write(state, 0x01C2, 0x0000); + /* register CMN_DIAG_PLL0_FBH_OVRD */ + reg_val = set_reg_value(cmnda_pll0_fb_div_high); + reg_val |= (1 << 15); + Afe_write(state, 0x01C0, reg_val); + /* register CMN_DIAG_PLL0_V2I_TUNE */ + reg_val = set_reg_value(voltage_to_current_coarse); + reg_val |= set_reg_value(voltage_to_current); + Afe_write(state, 0x01C5, reg_val); + /* register CMN_DIAG_PLL0_PTATIS_TUNE1 */ + reg_val = set_reg_value(pmos_ctrl); + reg_val |= set_reg_value(ndac_ctrl); + Afe_write(state, 0x01C8, reg_val); + /* register CMN_DIAG_PLL0_PTATIS_TUNE2 */ + reg_val = set_reg_value(ptat_ndac_ctrl); + Afe_write(state, 0x01C9, reg_val); + /* register CMN_DIAG_PLL0_CP_TUNE */ + reg_val = set_reg_value(charge_pump_gain); + Afe_write(state, 0x01C6, reg_val); + /* register CMN_DIAG_PLL0_LF_PROG */ + Afe_write(state, 0x01C7, 0x0008); + + /* register XCVR_DIAG_PLLDRC_CTRL */ + for (i = 0; i < num_lanes; i++) { + reg_val = Afe_read(state, 0x40E0 | (i << 9)); + reg_val &= 0xBFFF; + Afe_write(state, 0x40E0 | (i << 9), reg_val); + } + + /* register PHY_PMA_CMN_CTRL1 */ + reg_val = Afe_read(state, 0xC800); + reg_val &= 0xFF8F; + reg_val |= 0x0030; + Afe_write(state, 0xC800, reg_val); + + /*-------------------------------------------------------------------- + *--------------------Back to task phy_cfg_hdp------------------------ + *--------------------------------------------------------------------*/ + + if (phy_reset_workaround) { + /* register PHY_ISO_CMN_CTRL */ + Afe_write(state, 0xC010, 0x0001); // Deassert PHY reset */ + for (i = 0; i < num_lanes; i++) { + /* register TX_DIAG_ACYA */ + Afe_write(state, 0x41FF | (i << 9), 0x0001); + } + /* register PHY_PMA_ISO_CMN_CTRL */ + Afe_write(state, 0xC810, 0x0003); + for (i = 0; i < num_lanes; i++) { + /* register XCVR_PSM_RCTRL */ + Afe_write(state, 0x4001 | (i << 9), 0xFEFC); + } + /* register PHY_PMA_ISO_CMN_CTRL */ + Afe_write(state, 0xC810, 0x0013); /* Assert cmn_macro_pwr_en */ + + /* PHY_PMA_ISO_CMN_CTRL */ + while (!(Afe_read(state, 0xC810) & (1 << 5))) ; /* wait for cmn_macro_pwr_en_ack */ + + /* PHY_PMA_CMN_CTRL1 */ + while (!(Afe_read(state, 0xC800) & (1 << 0))) ; /* wait for cmn_ready */ + } else { + for (i = 0; i < num_lanes; i++) { + Afe_write(state, 0x41FF | (i << 9), 0x0001); + /* register XCVR_PSM_RCTRL */ + Afe_write(state, 0x4001 | (i << 9), 0xBEFC); + } + } + for (i = 0; i < num_lanes; i++) { + /* register TX_PSC_A0 */ + Afe_write(state, 0x4100 | (i << 9), 0x6791); + /* register TX_PSC_A1 */ + Afe_write(state, 0x4101 | (i << 9), 0x6790); + /* register TX_PSC_A2 */ + Afe_write(state, 0x4102 | (i << 9), 0x0090); + /* register TX_PSC_A3 */ + Afe_write(state, 0x4103 | (i << 9), 0x0090); + } + + /* register PHY_HDP_MODE_CTL */ + Afe_write(state, 0xC008, 0x0004); + return character_freq_khz; + +} + +int hdmi_tx_kiran_power_configuration_seq(state_struct *state, int num_lanes) +{ + /* Configure the power state. */ + + /* PHY_DP_MODE_CTL */ + while (!(Afe_read(state, 0xC008) & (1 << 6))) ; + + /* PHY_DP_MODE_CTL */ + Afe_write(state, 0xC008, (((0x0F << num_lanes) & 0x0F) << 12) | 0x0001); + + /* PHY_DP_MODE_CTL */ + while (!(Afe_read(state, 0xC008) & (1 << 4))) ; + return 0; +} diff --git a/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.h b/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.h new file mode 100644 index 00000000000000..6a334be62f0ea7 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/API_AFE_ss28fdsoi_kiran_hdmitx.h @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * API_AFE_ss28fdsoi_kiran_hdmitx.h + * + ****************************************************************************** + */ + +#ifndef API_AFE_SS28FDSOI_KIRAN_HDMITX_H_ +#define API_AFE_SS28FDSOI_KIRAN_HDMITX_H_ + +#include "../../../../mxc/hdp/all.h" + +int phy_cfg_hdp_ss28fdsoi(state_struct *state, int num_lanes, + VIC_MODES vicMode, int bpp, VIC_PXL_ENCODING_FORMAT format); +int hdmi_tx_kiran_power_configuration_seq(state_struct *state, int num_lanes); +int get_table_row_match_column(const u32 *array, u32 table_rows, + u32 table_cols, u32 start_row, + u32 column_to_search, + u32 value_to_search_in_column); +int get_table_row(const u32 *array, u32 table_rows, + u32 table_cols, u32 variable_in_range, + u32 range_min_column, u32 range_max_column, + u32 column_to_search, u32 column_value); + +#endif diff --git a/drivers/gpu/drm/imx/hdp/Kconfig b/drivers/gpu/drm/imx/hdp/Kconfig new file mode 100644 index 00000000000000..06854a2427bc1a --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/Kconfig @@ -0,0 +1,11 @@ +config DRM_IMX_HDP + tristate "IMX8 HD Display Controller" + select DRM_IMX + select MX8_HDP_DP + select DRM_KMS_HELPER + select VIDEOMODE_HELPERS + select DRM_GEM_CMA_HELPER + select DRM_KMS_CMA_HELPER + select DRM_PANEL + help + Choose this if you want to use DP/HDMI on i.MX8. diff --git a/drivers/gpu/drm/imx/hdp/Makefile b/drivers/gpu/drm/imx/hdp/Makefile new file mode 100644 index 00000000000000..49da5b9dc99f84 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/Makefile @@ -0,0 +1,5 @@ +obj-$(CONFIG_DRM_IMX_HDP) += imx-hdp.o \ + imx-dp.o imx-hdmi.o \ + API_AFE_ss28fdsoi_kiran_hdmitx.o \ + ss28fdsoi_hdmitx_table.o \ + API_AFE_mcu1_dp.o diff --git a/drivers/gpu/drm/imx/hdp/hdmitx_firmware.h b/drivers/gpu/drm/imx/hdp/hdmitx_firmware.h new file mode 100644 index 00000000000000..e62f9523de0028 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/hdmitx_firmware.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * This file was auto-generated from dram0.data and iram0.data firmware images + * Do not edit it manually. + * + ****************************************************************************** + * + * hdmitx_firmware.c + * + ****************************************************************************** + */ +#include <linux/io.h> + +static u32 const hdmitx_iram0[] = { + 0x00000000, +}; + +static u32 const hdmitx_dram0[] = { + 0x00000000, +}; + +u32 const *hdmitx_iram0_get_ptr(void) +{ + return (u32 const *)hdmitx_iram0; +} + +u32 const *hdmitx_dram0_get_ptr(void) +{ + return (u32 const *)hdmitx_dram0; +} + +size_t hdmitx_iram0_get_size(void) +{ + return sizeof(hdmitx_iram0); +} + +size_t hdmitx_dram0_get_size(void) +{ + return sizeof(hdmitx_dram0); +} diff --git a/drivers/gpu/drm/imx/hdp/imx-dp.c b/drivers/gpu/drm/imx/hdp/imx-dp.c new file mode 100644 index 00000000000000..8b360f572b6560 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-dp.c @@ -0,0 +1,196 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 "mhdp_firmware.h" +#include "imx-hdp.h" +#include "imx-hdmi.h" +#include "imx-dp.h" + +void dp_fw_load(state_struct *state) +{ + printk("loading hdmi firmware\n"); + CDN_API_LoadFirmware(state, + (u8 *)mhdp_iram0_get_ptr(), + mhdp_iram0_get_size(), + (u8 *)mhdp_dram0_get_ptr(), + mhdp_dram0_get_size()); +} + +void dp_fw_init(state_struct *state, u32 core_rate) +{ + u8 echo_msg[] = "echo test"; + u8 echo_resp[sizeof(echo_msg) + 1]; + int ret; + u8 resp; + + /* configure the clock */ + CDN_API_SetClock(state, core_rate/1000000); + printk("CDN_API_SetClock completed\n"); + + cdn_apb_write(state, APB_CTRL << 2, 0); + printk("Started firmware!\n"); + + ret = CDN_API_CheckAlive_blocking(state); + printk("CDN_API_CheckAlive returned (ret = %d)\n", ret); + + /* turn on IP activity */ + ret = CDN_API_MainControl_blocking(state, 1, &resp); + printk("CDN_API_MainControl_blocking (ret = %d resp = %u)\n", + ret, resp); + + ret = CDN_API_General_Test_Echo_Ext_blocking(state, echo_msg, echo_resp, + sizeof(echo_msg), CDN_BUS_TYPE_APB); + printk("CDN_API_General_Test_Echo_Ext_blocking (ret = %d echo_resp = %s)\n", + ret, echo_resp); + + /* Line swaping */ + CDN_API_General_Write_Register_blocking(state, + ADDR_SOURCD_PHY + (LANES_CONFIG << 2), 0x0040001b); + printk("CDN_API_General_Write_Register_blockin ... setting LANES_CONFIG\n"); +} + +void dp_phy_init(state_struct *state, int num_lanes, int max_link_rate, int tmp) +{ + int ret; + + /* PHY initialization while phy reset pin is active */ + AFE_init(state, num_lanes, (ENUM_AFE_LINK_RATE)max_link_rate); + printk("AFE_init\n"); + + /* In this point the phy reset should be deactivated */ + hdp_phy_reset(1); + printk("deasserted reset\n"); + + /* PHY power set */ + AFE_power(state, num_lanes, (ENUM_AFE_LINK_RATE)max_link_rate); + printk("AFE_power exit\n"); + + /* Video off */ + ret = CDN_API_DPTX_SetVideo_blocking(state, 0); + printk("CDN_API_DPTX_SetVideo_blocking (ret = %d)\n", ret); +} + +/* Max Link Rate: 06h (1.62Gbps), 0Ah (2.7Gbps), 14h (5.4Gbps), 1Eh (8.1Gbps)--N/A */ +void dp_mode_set(state_struct *state, int vic, int format, int color_depth, int max_link_rate) +{ + int ret; + + /* Set Host capabilities */ + /* Number of lanes and SSC */ + u8 num_lanes = 4; + u8 ssc = 0; + u8 scrambler = 0; + /* Max voltage swing */ + u8 max_vswing = 3; + u8 force_max_vswing = 0; + /* Max pre-emphasis */ + u8 max_preemph = 2; + u8 force_max_preemph = 0; + /* Supported test patterns mask */ + u8 supp_test_patterns = 0x0F; + /* AUX training? */ + u8 no_aux_training = 0; + /* Lane mapping */ + u8 lane_mapping = 0x1B; /* we have 4 lane, so it's OK */ + /* Extended Host capabilities */ + u8 ext_host_cap = 1; + /* Bits per sub-pixel */ + u8 bits_per_subpixel = 8; + /* Stereoscopic video */ + STEREO_VIDEO_ATTR stereo = 0; + /* B/W Balance Type: 0 no data, 1 IT601, 2 ITU709 */ + BT_TYPE bt_type = 0; + /* Transfer Unit */ + u8 transfer_unit = 64; + VIC_SYMBOL_RATE sym_rate; + + ret = CDN_API_DPTX_SetHostCap_blocking(state, + max_link_rate, + (num_lanes & 0x7) | ((ssc & 1) << 3) | ((scrambler & 1) << 4), + (max_vswing & 0x3) | ((force_max_vswing & 1) << 4), + (max_preemph & 0x3) | ((force_max_preemph & 1) << 4), + supp_test_patterns, + no_aux_training, //fast link training + lane_mapping, + ext_host_cap + ); + printk("CDN_API_DPTX_SetHostCap_blocking (ret = %d)\n", ret); + + switch (max_link_rate) { + case 0x0a: + sym_rate = RATE_2_7; + break; + case 0x14: + sym_rate = RATE_5_4; + break; + default: + sym_rate = RATE_1_6; + } + + ret = CDN_API_DPTX_Set_VIC_blocking(state, + vic, + bits_per_subpixel, + num_lanes, + sym_rate, + format, + stereo, + bt_type, + transfer_unit + ); + printk("CDN_API_DPTX_Set_VIC_blocking (ret = %d)\n", ret); + + ret = CDN_API_DPTX_TrainingControl_blocking(state, 1); + printk("CDN_API_DPTX_TrainingControl_blocking (ret = %d)\n", ret); + + /* Set video on */ + ret = CDN_API_DPTX_SetVideo_blocking(state, 1); + printk("CDN_API_DPTX_SetVideo_blocking (ret = %d)\n", ret); + + udelay(1000); +} + +int dp_get_edid_block(void *data, u8 *buf, unsigned int block, size_t len) +{ + DPTX_Read_EDID_response edidResp; + state_struct *state = data; + CDN_API_STATUS ret = 0; + + memset(&edidResp, 0, sizeof(edidResp)); + switch (block) { + case 0: + ret = CDN_API_DPTX_Read_EDID_blocking(state, 0, 0, &edidResp); + break; + case 1: + ret = CDN_API_DPTX_Read_EDID_blocking(state, 0, 1, &edidResp); + break; + case 2: + ret = CDN_API_DPTX_Read_EDID_blocking(state, 1, 0, &edidResp); + break; + case 3: + ret = CDN_API_DPTX_Read_EDID_blocking(state, 1, 1, &edidResp); + break; + default: + pr_warn("EDID block %x read not support\n", block); + } + + memcpy(buf, edidResp.buff, 128); + + return ret; +} + +void dp_get_hpd_state(state_struct *state, u8 *hpd) +{ + CDN_API_DPTX_GetHpdStatus_blocking(state, hpd); +} diff --git a/drivers/gpu/drm/imx/hdp/imx-dp.h b/drivers/gpu/drm/imx/hdp/imx-dp.h new file mode 100644 index 00000000000000..5709342af630d0 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-dp.h @@ -0,0 +1,24 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ +#ifndef _IMX_DP_H_ +#define _IMX_DP_H_ + +void dp_fw_load(state_struct *state); +void dp_fw_init(state_struct *state, u32 rate); +void dp_mode_set(state_struct *state, int vic, int format, int color_depth, int max_link_rate); +void dp_phy_init(state_struct *state, int num_lanes, int max_link_rate, int tmp); +int dp_get_edid_block(void *data, u8 *buf, u32 block, size_t len); +void dp_get_hpd_state(state_struct *state, u8 *hpd); + +#endif diff --git a/drivers/gpu/drm/imx/hdp/imx-hdmi.c b/drivers/gpu/drm/imx/hdp/imx-hdmi.c new file mode 100644 index 00000000000000..c89b6da031124f --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-hdmi.c @@ -0,0 +1,148 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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 "hdmitx_firmware.h" +#include "imx-hdp.h" +#include "imx-hdmi.h" +#include "API_AFE_ss28fdsoi_kiran_hdmitx.h" + +static int character_freq_khz; + +void hdmi_fw_load(state_struct *state) +{ + printk("loading hdmi firmware\n"); + CDN_API_LoadFirmware(state, + (u8 *)hdmitx_iram0_get_ptr(), + hdmitx_iram0_get_size(), + (u8 *)hdmitx_dram0_get_ptr(), + hdmitx_dram0_get_size()); +} + +void hdmi_fw_init(state_struct *state, u32 core_rate) +{ + u8 echo_msg[] = "echo test"; + u8 echo_resp[sizeof(echo_msg) + 1]; + int ret; + u8 sts; + + /* configure the clock */ + CDN_API_SetClock(state, core_rate/1000000); + printk("CDN_API_SetClock completed\n"); + + /* moved from CDN_API_LoadFirmware */ + cdn_apb_write(state, APB_CTRL << 2, 0); + printk("Started firmware!\n"); + + ret = CDN_API_CheckAlive_blocking(state); + printk("CDN_API_CheckAlive returned ret = %d\n", ret); + + /* turn on IP activity */ + ret = CDN_API_MainControl_blocking(state, 1, &sts); + printk("CDN_API_MainControl_blocking ret = %d sts = %u\n", ret, sts); + + ret = CDN_API_General_Test_Echo_Ext_blocking(state, echo_msg, echo_resp, + sizeof(echo_msg), CDN_BUS_TYPE_APB); + printk("CDN_API_General_Test_Echo_Ext_blocking - APB(ret = %d echo_resp = %s)\n", + ret, echo_resp); +} + +void hdmi_phy_init(state_struct *state, int vic, int format, int color_depth) +{ + int ret; + + /* Configure PHY */ + character_freq_khz = phy_cfg_hdp_ss28fdsoi(state, 4, vic, color_depth, format); + + hdp_phy_reset(1); + + hdmi_tx_kiran_power_configuration_seq(state, 4); + + /* Set the lane swapping */ + ret = CDN_API_General_Write_Register_blocking(state, ADDR_SOURCD_PHY + (LANES_CONFIG << 2), + F_SOURCE_PHY_LANE0_SWAP(3) | F_SOURCE_PHY_LANE1_SWAP(0) | + F_SOURCE_PHY_LANE2_SWAP(1) | F_SOURCE_PHY_LANE3_SWAP(2) | + F_SOURCE_PHY_COMB_BYPASS(0) | F_SOURCE_PHY_20_10(1)); + printk("CDN_API_General_Write_Register_blocking LANES_CONFIG ret = %d\n", ret); +} + +void hdmi_mode_set(state_struct *state, int vic, int format, int color_depth, int temp) +{ + int ret; + GENERAL_Read_Register_response regresp; + + /* B/W Balance Type: 0 no data, 1 IT601, 2 ITU709 */ + BT_TYPE bw_type = 0; + /* Mode = 0 - DVI, 1 - HDMI1.4, 2 HDMI 2.0 */ + HDMI_TX_MAIL_HANDLER_PROTOCOL_TYPE ptype = 1; + + if (vic == VIC_MODE_97_60Hz) + ptype = 2; + + ret = CDN_API_HDMITX_Init_blocking(state); + printk("CDN_API_STATUS CDN_API_HDMITX_Init_blocking ret = %d\n", ret); + + /* Set HDMI TX Mode */ + ret = CDN_API_HDMITX_Set_Mode_blocking(state, ptype, character_freq_khz); + printk("CDN_API_HDMITX_Set_Mode_blocking ret = %d\n", ret); + + ret = CDN_API_Set_AVI(state, vic, format, bw_type); + printk("CDN_API_Set_AVI ret = %d\n", ret); + + ret = CDN_API_HDMITX_SetVic_blocking(state, vic, color_depth, format); + printk("CDN_API_HDMITX_SetVic_blocking ret = %d\n", ret); + + /* adjust the vsync/hsync polarity */ + CDN_API_General_Read_Register_blocking( + state, ADDR_SOURCE_VIF + (HSYNC2VSYNC_POL_CTRL << 2), ®resp); + printk("Initial HSYNC2VSYNC_POL_CTRL: 0x%x\n", regresp.val); + if ((regresp.val & 0x6) != 0) { + __raw_writel(0x4, state->mem.ss_base); + } + msleep(50); +} + +int hdmi_get_edid_block(void *data, u8 *buf, u32 block, size_t len) +{ + HDMITX_TRANS_DATA edidResp; + state_struct *state = data; + CDN_API_STATUS ret = 0; + + memset(&edidResp, 0, sizeof(edidResp)); + switch (block) { + case 0: + ret = CDN_API_HDMITX_READ_EDID_blocking(state, 0, 0, &edidResp); + break; + case 1: + ret = CDN_API_HDMITX_READ_EDID_blocking(state, 0, 1, &edidResp); + break; + case 2: + ret = CDN_API_HDMITX_READ_EDID_blocking(state, 1, 0, &edidResp); + break; + case 3: + ret = CDN_API_HDMITX_READ_EDID_blocking(state, 1, 1, &edidResp); + break; + default: + printk("EDID block %x read not support\n", block); + } + + memcpy(buf, edidResp.buff, 128); + + return ret; +} + +void hdmi_get_hpd_state(state_struct *state, u8 *hpd) +{ + CDN_API_HDMITX_GetHpdStatus_blocking(state, hpd); +} diff --git a/drivers/gpu/drm/imx/hdp/imx-hdmi.h b/drivers/gpu/drm/imx/hdp/imx-hdmi.h new file mode 100644 index 00000000000000..86c9a1b9cbf4ad --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-hdmi.h @@ -0,0 +1,23 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ +#ifndef _IMX_HDMI_H_ +#define _IMX_HDMI_H_ +void hdmi_fw_load(state_struct *state); +void hdmi_fw_init(state_struct *state, u32 rate); +void hdmi_phy_init(state_struct *state, int vic, int format, int color_depth); +void hdmi_mode_set(state_struct *state, int vic, int format, int color_depth, int max_link); +int hdmi_get_edid_block(void *data, u8 *buf, u32 block, size_t len); +void hdmi_get_hpd_state(state_struct *state, u8 *hpd); + +#endif diff --git a/drivers/gpu/drm/imx/hdp/imx-hdp.c b/drivers/gpu/drm/imx/hdp/imx-hdp.c new file mode 100644 index 00000000000000..e817c4d0e4ed0d --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-hdp.c @@ -0,0 +1,1022 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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/kthread.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/component.h> +#include <linux/mfd/syscon.h> +#include <linux/of.h> +#include <linux/of_device.h> + +#include "imx-hdp.h" +#include "imx-hdmi.h" +#include "imx-dp.h" +#include "../imx-drm.h" + +struct drm_display_mode *g_mode; + +static const struct drm_display_mode edid_cea_modes = { + /* 16 - 1920x1080@60Hz */ + DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 148500, 1920, 2008, + 2052, 2200, 0, 1080, 1084, 1089, 1125, 0, + DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC), + .vrefresh = 60, .picture_aspect_ratio = HDMI_PICTURE_ASPECT_16_9, +}; + +static inline struct imx_hdp *enc_to_imx_hdp(struct drm_encoder *e) +{ + return container_of(e, struct imx_hdp, encoder); +} + +static void imx_hdp_plmux_config(struct imx_hdp *hdp, struct drm_display_mode *mode) +{ + u32 val; + + val = 4; /* RGB */ + if (mode->flags & DRM_MODE_FLAG_PVSYNC) + val |= 1 << PL_MUX_CTL_VCP_OFFSET; + if (mode->flags & DRM_MODE_FLAG_PHSYNC) + val |= 1 << PL_MUX_CTL_HCP_OFFSET; + if (mode->flags & DRM_MODE_FLAG_INTERLACE) + val |= 0x2; + + writel(val, hdp->ss_base + CSR_PIXEL_LINK_MUX_CTL); +} + +static void imx_hdp_state_init(struct imx_hdp *hdp) +{ + state_struct *state = &hdp->state; + + memset(state, 0, sizeof(state_struct)); + mutex_init(&state->mutex); + + state->mem.regs_base = hdp->regs_base; + state->mem.ss_base = hdp->ss_base; + state->rw = hdp->rw; +} + +void hdp_phy_reset(u8 reset) +{ + sc_err_t sciErr; + sc_ipc_t ipcHndl = 0; + u32 mu_id; + + sciErr = sc_ipc_getMuID(&mu_id); + if (sciErr != SC_ERR_NONE) { + pr_err("Cannot obtain MU ID\n"); + return; + } + + sciErr = sc_ipc_open(&ipcHndl, mu_id); + if (sciErr != SC_ERR_NONE) { + pr_err("sc_ipc_open failed! (sciError = %d)\n", sciErr); + return; + } + + /* set the pixel link mode and pixel type */ + sc_misc_set_control(ipcHndl, SC_R_HDMI, SC_C_PHY_RESET, reset); + if (sciErr != SC_ERR_NONE) + pr_err("SC_R_HDMI PHY reset failed %d!\n", sciErr); + + sc_ipc_close(mu_id); +} + +static void clk_set_root(struct imx_hdp *hdp) +{ + sc_ipc_t ipcHndl = hdp->ipcHndl; + + /* set clock to bypass mode, source from av pll */ + /* those clock default source from dig pll */ + /* HDMI DI Pixel Link Mux Clock */ + sc_pm_set_clock_parent(ipcHndl, SC_R_HDMI, SC_PM_CLK_MISC0, 4); + /* HDMI DI Pixel Link Clock */ + sc_pm_set_clock_parent(ipcHndl, SC_R_HDMI, SC_PM_CLK_MISC1, 4); + /* HDMI DI Pixel Clock */ + sc_pm_set_clock_parent(ipcHndl, SC_R_HDMI, SC_PM_CLK_MISC3, 4); +} + +static void hdp_ipg_clock_set_rate(struct imx_hdp *hdp) +{ + u32 clk_rate; + + if (hdp->is_hdmi == true) { + /* HDMI */ + clk_set_root(hdp); + clk_set_rate(hdp->clks.dig_pll, PLL_675MHZ); + clk_set_rate(hdp->clks.clk_core, PLL_675MHZ/5); + clk_set_rate(hdp->clks.clk_ipg, PLL_675MHZ/8); + /* Default pixel clock for HDMI */ + clk_set_rate(hdp->clks.av_pll, 148500000); + } else { + /* DP */ + clk_set_rate(hdp->clks.av_pll, 24000000); + clk_rate = clk_get_rate(hdp->clks.dig_pll); + printk("dig_pll= %d\n", clk_rate); + if (clk_rate == PLL_1188MHZ) { + clk_set_rate(hdp->clks.dig_pll, PLL_1188MHZ); + clk_set_rate(hdp->clks.clk_core, PLL_1188MHZ/10); + clk_set_rate(hdp->clks.clk_ipg, PLL_1188MHZ/14); + } else { + clk_set_rate(hdp->clks.dig_pll, PLL_675MHZ); + clk_set_rate(hdp->clks.clk_core, PLL_675MHZ/5); + clk_set_rate(hdp->clks.clk_ipg, PLL_675MHZ/8); + } + } +} + +static void dp_pixel_clock_set_rate(struct imx_hdp *hdp) +{ + unsigned int pclock = hdp->video.cur_mode.clock * 1000; + u32 ret; + + /* 24MHz for DP and pixel clock for HDMI */ + if (hdp->dual_mode == true) { + clk_set_rate(hdp->clks.clk_pxl, pclock/2); + clk_set_rate(hdp->clks.clk_pxl_link, pclock/2); + } else { + ret = clk_set_rate(hdp->clks.clk_pxl, pclock); + if (ret < 0) + printk("clk_pxl set failed T %u,A %lu", pclock, clk_get_rate(hdp->clks.clk_pxl)); + clk_set_rate(hdp->clks.clk_pxl_link, pclock); + } + clk_set_rate(hdp->clks.clk_pxl_mux, pclock); +} + +static int dp_clock_init(struct imx_hdp *hdp) +{ + struct device *dev = hdp->dev; + + hdp->clks.av_pll = devm_clk_get(dev, "av_pll"); + if (IS_ERR(hdp->clks.av_pll)) { + dev_err(dev, "failed to get av pll clk\n"); + return PTR_ERR(hdp->clks.av_pll); + } + + hdp->clks.dig_pll = devm_clk_get(dev, "dig_pll"); + if (IS_ERR(hdp->clks.dig_pll)) { + dev_err(dev, "failed to get dig pll clk\n"); + return PTR_ERR(hdp->clks.dig_pll); + } + + hdp->clks.clk_ipg = devm_clk_get(dev, "clk_ipg"); + if (IS_ERR(hdp->clks.clk_ipg)) { + dev_err(dev, "failed to get dp ipg clk\n"); + return PTR_ERR(hdp->clks.clk_ipg); + } + + hdp->clks.clk_core = devm_clk_get(dev, "clk_core"); + if (IS_ERR(hdp->clks.clk_core)) { + dev_err(dev, "failed to get hdp core clk\n"); + return PTR_ERR(hdp->clks.clk_core); + } + + hdp->clks.clk_pxl = devm_clk_get(dev, "clk_pxl"); + if (IS_ERR(hdp->clks.clk_pxl)) { + dev_err(dev, "failed to get pxl clk\n"); + return PTR_ERR(hdp->clks.clk_pxl); + } + + hdp->clks.clk_pxl_mux = devm_clk_get(dev, "clk_pxl_mux"); + if (IS_ERR(hdp->clks.clk_pxl_mux)) { + dev_err(dev, "failed to get pxl mux clk\n"); + return PTR_ERR(hdp->clks.clk_pxl_mux); + } + + hdp->clks.clk_pxl_link = devm_clk_get(dev, "clk_pxl_link"); + if (IS_ERR(hdp->clks.clk_pxl_mux)) { + dev_err(dev, "failed to get pxl link clk\n"); + return PTR_ERR(hdp->clks.clk_pxl_link); + } + + hdp->clks.clk_hdp = devm_clk_get(dev, "clk_hdp"); + if (IS_ERR(hdp->clks.clk_hdp)) { + dev_err(dev, "failed to get hdp clk\n"); + return PTR_ERR(hdp->clks.clk_hdp); + } + + hdp->clks.clk_phy = devm_clk_get(dev, "clk_phy"); + if (IS_ERR(hdp->clks.clk_phy)) { + dev_err(dev, "failed to get phy clk\n"); + return PTR_ERR(hdp->clks.clk_phy); + } + hdp->clks.clk_apb = devm_clk_get(dev, "clk_apb"); + if (IS_ERR(hdp->clks.clk_apb)) { + dev_err(dev, "failed to get apb clk\n"); + return PTR_ERR(hdp->clks.clk_apb); + } + hdp->clks.clk_lis = devm_clk_get(dev, "clk_lis"); + if (IS_ERR(hdp->clks.clk_lis)) { + dev_err(dev, "failed to get lis clk\n"); + return PTR_ERR(hdp->clks.clk_lis); + } + hdp->clks.clk_msi = devm_clk_get(dev, "clk_msi"); + if (IS_ERR(hdp->clks.clk_msi)) { + dev_err(dev, "failed to get msi clk\n"); + return PTR_ERR(hdp->clks.clk_msi); + } + hdp->clks.clk_lpcg = devm_clk_get(dev, "clk_lpcg"); + if (IS_ERR(hdp->clks.clk_lpcg)) { + dev_err(dev, "failed to get lpcg clk\n"); + return PTR_ERR(hdp->clks.clk_lpcg); + } + hdp->clks.clk_even = devm_clk_get(dev, "clk_even"); + if (IS_ERR(hdp->clks.clk_even)) { + dev_err(dev, "failed to get even clk\n"); + return PTR_ERR(hdp->clks.clk_even); + } + hdp->clks.clk_dbl = devm_clk_get(dev, "clk_dbl"); + if (IS_ERR(hdp->clks.clk_dbl)) { + dev_err(dev, "failed to get dbl clk\n"); + return PTR_ERR(hdp->clks.clk_dbl); + } + hdp->clks.clk_vif = devm_clk_get(dev, "clk_vif"); + if (IS_ERR(hdp->clks.clk_vif)) { + dev_err(dev, "failed to get vif clk\n"); + return PTR_ERR(hdp->clks.clk_vif); + } + hdp->clks.clk_apb_csr = devm_clk_get(dev, "clk_apb_csr"); + if (IS_ERR(hdp->clks.clk_apb_csr)) { + dev_err(dev, "failed to get apb csr clk\n"); + return PTR_ERR(hdp->clks.clk_apb_csr); + } + hdp->clks.clk_apb_ctrl = devm_clk_get(dev, "clk_apb_ctrl"); + if (IS_ERR(hdp->clks.clk_apb_ctrl)) { + dev_err(dev, "failed to get apb ctrl clk\n"); + return PTR_ERR(hdp->clks.clk_apb_ctrl); + } + + return true; +} + +static int dp_pixel_clock_enable(struct imx_hdp *hdp) +{ + struct device *dev = hdp->dev; + int ret; + + ret = clk_prepare_enable(hdp->clks.av_pll); + if (ret < 0) { + dev_err(dev, "%s, pre clk pxl error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_pxl); + if (ret < 0) { + dev_err(dev, "%s, pre clk pxl error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_pxl_mux); + if (ret < 0) { + dev_err(dev, "%s, pre clk pxl mux error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_pxl_link); + if (ret < 0) { + dev_err(dev, "%s, pre clk pxl link error\n", __func__); + return ret; + } + return ret; + +} + +static void dp_pixel_clock_disable(struct imx_hdp *hdp) +{ + clk_disable_unprepare(hdp->clks.clk_pxl); + clk_disable_unprepare(hdp->clks.clk_pxl_link); + clk_disable_unprepare(hdp->clks.clk_pxl_mux); +} + +static int dp_ipg_clock_enable(struct imx_hdp *hdp) +{ + struct device *dev = hdp->dev; + int ret; + + ret = clk_prepare_enable(hdp->clks.av_pll); + if (ret < 0) { + dev_err(dev, "%s, pre av pll error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.dig_pll); + if (ret < 0) { + dev_err(dev, "%s, pre dig pll error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_ipg); + if (ret < 0) { + dev_err(dev, "%s, pre clk_ipg error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_core); + if (ret < 0) { + dev_err(dev, "%s, pre clk core error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_hdp); + if (ret < 0) { + dev_err(dev, "%s, pre clk hdp error\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_phy); + if (ret < 0) { + dev_err(dev, "%s, pre clk phy\n", __func__); + return ret; + } + + ret = clk_prepare_enable(hdp->clks.clk_apb); + if (ret < 0) { + dev_err(dev, "%s, pre clk apb error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_lis); + if (ret < 0) { + dev_err(dev, "%s, pre clk lis error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_lpcg); + if (ret < 0) { + dev_err(dev, "%s, pre clk lpcg error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_msi); + if (ret < 0) { + dev_err(dev, "%s, pre clk msierror\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_even); + if (ret < 0) { + dev_err(dev, "%s, pre clk even error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_dbl); + if (ret < 0) { + dev_err(dev, "%s, pre clk dbl error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_vif); + if (ret < 0) { + dev_err(dev, "%s, pre clk vif error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_apb_csr); + if (ret < 0) { + dev_err(dev, "%s, pre clk apb csr error\n", __func__); + return ret; + } + ret = clk_prepare_enable(hdp->clks.clk_apb_ctrl); + if (ret < 0) { + dev_err(dev, "%s, pre clk apb ctrl error\n", __func__); + return ret; + } + return ret; +} + +static void dp_pixel_link_config(struct imx_hdp *hdp) +{ + sc_ipc_t ipcHndl = hdp->ipcHndl; + + /* config dpu1 di0 to hdmi/dp mode */ + sc_misc_set_control(ipcHndl, SC_R_DC_0, SC_C_PXL_LINK_MST1_ADDR, 1); + sc_misc_set_control(ipcHndl, SC_R_DC_0, SC_C_PXL_LINK_MST1_VLD, 1); + sc_misc_set_control(ipcHndl, SC_R_DC_0, SC_C_SYNC_CTRL0, 1); +} + +static int imx_hdp_deinit(struct imx_hdp *hdp) +{ + u8 bresp; + u32 ret; + + /* Stop link training */ + CDN_API_DPTX_TrainingControl_blocking(&hdp->state, 0); + + /* Disable HPD event and training */ + CDN_API_DPTX_EnableEvent_blocking(&hdp->state, 0, 0); + + /* turn off hdp controller IP activity 0-standby */ + ret = CDN_API_MainControl_blocking(&hdp->state, 0, &bresp); + if (ret != CDN_OK) + return -1; + + return ret; +} + +static int imx_get_vic_index(struct drm_display_mode *mode) +{ + int i; + + for (i = 0; i < VIC_MODE_COUNT; i++) { + if (mode->hdisplay == vic_table[i][H_ACTIVE] && + mode->vdisplay == vic_table[i][V_ACTIVE] && + mode->clock == vic_table[i][PIXEL_FREQ_KHZ]) + return i; + } + /* Default 1080p60 */ + printk("default vic 2\n"); + return 2; +} + +static void imx_hdp_mode_setup(struct imx_hdp *hdp, struct drm_display_mode *mode) +{ + int dp_vic; + + dp_pixel_clock_set_rate(hdp); + dp_pixel_clock_enable(hdp); + + imx_hdp_plmux_config(hdp, mode); + + dp_vic = imx_get_vic_index(mode); + + imx_hdp_call(hdp, mode_set, &hdp->state, dp_vic, 1, 8, hdp->link_rate); +} + +static int imx_hdp_cable_plugin(struct imx_hdp *hdp) +{ + return 0; +} + +static int imx_hdp_cable_plugout(struct imx_hdp *hdp) +{ + dp_pixel_clock_disable(hdp); + return 0; +} + + +static void imx_hdp_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *orig_mode, + struct drm_display_mode *mode) +{ + struct imx_hdp *hdp = bridge->driver_private; + + printk("%s++\n", __func__); + mutex_lock(&hdp->mutex); + + memcpy(&hdp->video.cur_mode, mode, sizeof(hdp->video.cur_mode)); + imx_hdp_mode_setup(hdp, mode); + /* Store the display mode for plugin/DKMS poweron events */ + memcpy(&hdp->video.pre_mode, mode, sizeof(hdp->video.pre_mode)); + + mutex_unlock(&hdp->mutex); +} + +static void imx_hdp_bridge_disable(struct drm_bridge *bridge) +{ +} + +static void imx_hdp_bridge_enable(struct drm_bridge *bridge) +{ +} + +static enum drm_connector_status +imx_hdp_connector_detect(struct drm_connector *connector, bool force) +{ + return connector_status_connected; +} + +static int imx_hdp_connector_get_modes(struct drm_connector *connector) +{ + struct drm_display_mode *mode; + int num_modes = 0; +#ifdef edid_enable + struct imx_hdp *hdp = container_of(connector, struct imx_hdp, + connector); + struct edid *edid; + + edid = drm_do_get_edid(connector, hdp->ops->get_edid_block, &hdp->state); + if (edid) { + dev_dbg(hdp->dev, "got edid: width[%d] x height[%d]\n", + edid->width_cm, edid->height_cm); + + printk("edid_head %x,%x,%x,%x,%x,%x,%x,%x\n", + edid->header[0], edid->header[1], edid->header[2], edid->header[3], + edid->header[4], edid->header[5], edid->header[6], edid->header[7]); + drm_mode_connector_update_edid_property(connector, edid); + ret = drm_add_edid_modes(connector, edid); + /* Store the ELD */ + drm_edid_to_eld(connector, edid); + kfree(edid); + } else { + dev_dbg(hdp->dev, "failed to get edid\n"); +#endif + mode = drm_mode_create(connector->dev); + if (!mode) + return -EINVAL; + drm_mode_copy(mode, &edid_cea_modes); + mode->type |= DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED; + drm_mode_probed_add(connector, mode); + num_modes = 1; +#ifdef edid_enable + } +#endif + + return num_modes; +} + +static enum drm_mode_status +imx_hdp_connector_mode_valid(struct drm_connector *connector, + struct drm_display_mode *mode) +{ + enum drm_mode_status mode_status = MODE_OK; + + if (mode->clock > 150000) + return MODE_CLOCK_HIGH; + + return mode_status; +} + +static void imx_hdp_connector_force(struct drm_connector *connector) +{ + struct imx_hdp *hdp = container_of(connector, struct imx_hdp, + connector); + + mutex_lock(&hdp->mutex); + hdp->force = connector->force; + mutex_unlock(&hdp->mutex); +} + +static const struct drm_connector_funcs imx_hdp_connector_funcs = { + .fill_modes = drm_helper_probe_single_connector_modes, + .detect = imx_hdp_connector_detect, + .destroy = drm_connector_cleanup, + .force = imx_hdp_connector_force, + .reset = drm_atomic_helper_connector_reset, + .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, + .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, +}; + +static const struct drm_connector_helper_funcs imx_hdp_connector_helper_funcs = { + .get_modes = imx_hdp_connector_get_modes, + .mode_valid = imx_hdp_connector_mode_valid, +}; + +static const struct drm_bridge_funcs imx_hdp_bridge_funcs = { + .enable = imx_hdp_bridge_enable, + .disable = imx_hdp_bridge_disable, + .mode_set = imx_hdp_bridge_mode_set, +}; + + +static void imx_hdp_imx_encoder_disable(struct drm_encoder *encoder) +{ +} + +static void imx_hdp_imx_encoder_enable(struct drm_encoder *encoder) +{ +} + +static int imx_hdp_imx_encoder_atomic_check(struct drm_encoder *encoder, + struct drm_crtc_state *crtc_state, + struct drm_connector_state *conn_state) +{ + struct imx_crtc_state *imx_crtc_state = to_imx_crtc_state(crtc_state); + + printk("%s++\n", __func__); + imx_crtc_state->bus_format = MEDIA_BUS_FMT_RGB101010_1X30; + return 0; +} + +static const struct drm_encoder_helper_funcs imx_hdp_imx_encoder_helper_funcs = { + .enable = imx_hdp_imx_encoder_enable, + .disable = imx_hdp_imx_encoder_disable, + .atomic_check = imx_hdp_imx_encoder_atomic_check, +}; + +static const struct drm_encoder_funcs imx_hdp_imx_encoder_funcs = { + .destroy = drm_encoder_cleanup, +}; + +static int mx8mq_hdp_read(struct hdp_mem *mem, unsigned int addr, unsigned int *value) +{ + unsigned int temp; + void *tmp_addr = mem->regs_base + addr; + temp = __raw_readl((volatile unsigned int *)tmp_addr); + *value = temp; + return 0; +} + +static int mx8mq_hdp_write(struct hdp_mem *mem, unsigned int addr, unsigned int value) +{ + void *tmp_addr = mem->regs_base + addr; + + __raw_writel(value, (volatile unsigned int *)tmp_addr); + return 0; +} + +static int mx8mq_hdp_sread(struct hdp_mem *mem, unsigned int addr, unsigned int *value) +{ + unsigned int temp; + void *tmp_addr = mem->ss_base + addr; + temp = __raw_readl((volatile unsigned int *)tmp_addr); + *value = temp; + return 0; +} + +static int mx8mq_hdp_swrite(struct hdp_mem *mem, unsigned int addr, unsigned int value) +{ + void *tmp_addr = mem->ss_base + addr; + __raw_writel(value, (volatile unsigned int *)tmp_addr); + return 0; +} + +static int mx8qm_hdp_read(struct hdp_mem *mem, unsigned int addr, unsigned int *value) +{ + unsigned int temp; + void *tmp_addr = (addr & 0xfff) + mem->regs_base; + void *off_addr = 0x8 + mem->ss_base;; + + __raw_writel(addr >> 12, off_addr); + temp = __raw_readl((volatile unsigned int *)tmp_addr); + + *value = temp; + return 0; +} + +static int mx8qm_hdp_write(struct hdp_mem *mem, unsigned int addr, unsigned int value) +{ + void *tmp_addr = (addr & 0xfff) + mem->regs_base; + void *off_addr = 0x8 + mem->ss_base;; + + __raw_writel(addr >> 12, off_addr); + + __raw_writel(value, (volatile unsigned int *) tmp_addr); + + return 0; +} + +static int mx8qm_hdp_sread(struct hdp_mem *mem, unsigned int addr, unsigned int *value) +{ + unsigned int temp; + void *tmp_addr = (addr & 0xfff) + mem->regs_base; + void *off_addr = 0xc + mem->ss_base;; + + __raw_writel(addr >> 12, off_addr); + + temp = __raw_readl((volatile unsigned int *)tmp_addr); + *value = temp; + return 0; +} + +static int mx8qm_hdp_swrite(struct hdp_mem *mem, unsigned int addr, unsigned int value) +{ + void *tmp_addr = (addr & 0xfff) + mem->regs_base; + void *off_addr = 0xc + mem->ss_base; + + __raw_writel(addr >> 12, off_addr); + __raw_writel(value, (volatile unsigned int *)tmp_addr); + + return 0; +} + +static struct hdp_rw_func imx8qm_rw = { + .read_reg = mx8qm_hdp_read, + .write_reg = mx8qm_hdp_write, + .sread_reg = mx8qm_hdp_sread, + .swrite_reg = mx8qm_hdp_swrite, +}; + +static struct hdp_ops imx8qm_dp_ops = { + .fw_load = dp_fw_load, + .fw_init = dp_fw_init, + .phy_init = dp_phy_init, + .mode_set = dp_mode_set, + .get_edid_block = dp_get_edid_block, +}; + +static struct hdp_ops imx8qm_hdmi_ops = { + .fw_load = hdmi_fw_load, + .fw_init = hdmi_fw_init, + .phy_init = hdmi_phy_init, + .mode_set = hdmi_mode_set, + .get_edid_block = hdmi_get_edid_block, +}; + +static struct hdp_devtype imx8qm_dp_devtype = { + .load_fw = true, + .is_hdmi = false, + .ops = &imx8qm_dp_ops, + .rw = &imx8qm_rw, +}; + +static struct hdp_devtype imx8qm_hdmi_devtype = { + .load_fw = true, + .is_hdmi = true, + .ops = &imx8qm_hdmi_ops, + .rw = &imx8qm_rw, +}; + +static struct hdp_rw_func imx8mq_rw = { + .read_reg = mx8mq_hdp_read, + .write_reg = mx8mq_hdp_write, + .sread_reg = mx8mq_hdp_sread, + .swrite_reg = mx8mq_hdp_swrite, +}; + +static struct hdp_ops imx8mq_ops = { +}; + +static struct hdp_devtype imx8mq_hdmi_devtype = { + .load_fw = false, + .is_hdmi = true, + .ops = &imx8mq_ops, + .rw = &imx8mq_rw, +}; + +static const struct of_device_id imx_hdp_dt_ids[] = { + { .compatible = "fsl,imx8qm-hdmi", .data = &imx8qm_hdmi_devtype}, + { .compatible = "fsl,imx8qm-dp", .data = &imx8qm_dp_devtype}, + { .compatible = "fsl,imx8mq-hdmi", .data = &imx8mq_hdmi_devtype}, + { } +}; +MODULE_DEVICE_TABLE(of, imx_hdp_dt_ids); + +#ifdef hdp_irq +static irqreturn_t imx_hdp_irq_handler(int irq, void *data) +{ + struct imx_hdp *hdp = data; + u8 eventId; + u8 HPDevents; + u8 aux_sts; + u8 aux_hpd; + u32 evt; + u8 hpdevent; + + CDN_API_Get_Event(&hdp->state, &evt); + + if (evt & 0x1) { + /* HPD event */ + printk("\nevt=%d\n", evt); + drm_helper_hpd_irq_event(hdp->connector.dev); + CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents); + printk("ReadEvent ID = %d HPD = %d\n", eventId, HPDevents); + CDN_API_DPTX_GetHpdStatus_blocking(&hdp->state, &aux_hpd); + printk("aux_hpd = 0xx\n", aux_hpd); + } else if (evt & 0x2) { + /* Link training event */ + } else + printk(".\r"); + + return IRQ_HANDLED; +} +#else +static int hpd_det_worker(void *_dp) +{ + struct imx_hdp *hdp = (struct imx_hdp *) _dp; + u8 eventId; + u8 HPDevents; + u8 aux_hpd; + u32 evt; + + for (;;) { + CDN_API_Get_Event(&hdp->state, &evt); + if (evt & 0x1) { + /* HPD event */ + CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents); + printk("ReadEvent ID = %d HPD = %d\n", eventId, HPDevents); + CDN_API_DPTX_GetHpdStatus_blocking(&hdp->state, &aux_hpd); + if (HPDevents & 0x1) { + printk("cable plugin\n"); + imx_hdp_cable_plugin(hdp); + hdp->cable_state = true; + drm_kms_helper_hotplug_event(hdp->connector.dev); + imx_hdp_mode_setup(hdp, &hdp->video.cur_mode); + } else if (HPDevents & 0x2) { + printk("cable plugout\n"); + hdp->cable_state = false; + imx_hdp_cable_plugout(hdp); + drm_kms_helper_hotplug_event(hdp->connector.dev); + } else + printk("HPDevent=0x%x\n", HPDevents); + } else if (evt & 0x2) { + /* Link training event */ + printk("evt=0x%x\n", evt); + CDN_API_DPTX_ReadEvent_blocking(&hdp->state, &eventId, &HPDevents); + printk("ReadEvent ID = %d HPD = %d\n", eventId, HPDevents); + } else if (evt & 0xf) + printk("evt=0x%x\n", evt); + + schedule_timeout_idle(200); + } + + return 0; +} +#endif + +static int imx_hdp_imx_bind(struct device *dev, struct device *master, + void *data) +{ + struct platform_device *pdev = to_platform_device(dev); + struct drm_device *drm = data; + struct imx_hdp *hdp; + const struct of_device_id *of_id = + of_match_device(imx_hdp_dt_ids, dev); + const struct hdp_devtype *devtype = of_id->data; + struct drm_encoder *encoder; + struct drm_bridge *bridge; + struct drm_connector *connector; + struct resource *res; + struct task_struct *hpd_worker; + int irq; + int ret; + sc_err_t sciErr; + u32 core_rate; + + if (!pdev->dev.of_node) + return -ENODEV; + + hdp = devm_kzalloc(&pdev->dev, sizeof(*hdp), GFP_KERNEL); + if (!hdp) + return -ENOMEM; + + hdp->dev = &pdev->dev; + encoder = &hdp->encoder; + bridge = &hdp->bridge; + connector = &hdp->connector; + + mutex_init(&hdp->mutex); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + dev_err(&pdev->dev, "can't get irq number\n"); + return irq; + } + + /* register map */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hdp->regs_base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdp->regs_base)) { + dev_err(dev, "Failed to get HDP CTRL base register\n"); + return -EINVAL; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + hdp->ss_base = devm_ioremap_resource(dev, res); + if (IS_ERR(hdp->ss_base)) { + dev_err(dev, "Failed to get HDP CRS base register\n"); + return -EINVAL; + } + + hdp->load_fw = devtype->load_fw; + hdp->is_hdmi = devtype->is_hdmi; + hdp->ops = devtype->ops; + hdp->rw = devtype->rw; + + /* encoder */ + encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); + /* + * If we failed to find the CRTC(s) which this encoder is + * supposed to be connected to, it's because the CRTC has + * not been registered yet. Defer probing, and hope that + * the required CRTC is added later. + */ + if (encoder->possible_crtcs == 0) { + return -EPROBE_DEFER; + } + + /* encoder */ + drm_encoder_helper_add(encoder, &imx_hdp_imx_encoder_helper_funcs); + drm_encoder_init(drm, encoder, &imx_hdp_imx_encoder_funcs, + DRM_MODE_ENCODER_TMDS, NULL); + + /* bridge */ + bridge->driver_private = hdp; + bridge->funcs = &imx_hdp_bridge_funcs; + ret = drm_bridge_attach(encoder, bridge, NULL); + if (ret) { + DRM_ERROR("Failed to initialize bridge with drm\n"); + return -EINVAL; + } + + encoder->bridge = bridge; + + /* connector */ + drm_connector_helper_add(connector, + &imx_hdp_connector_helper_funcs); + + drm_connector_init(drm, connector, + &imx_hdp_connector_funcs, + DRM_MODE_CONNECTOR_HDMIA); + + drm_mode_connector_attach_encoder(connector, encoder); + + dev_set_drvdata(dev, hdp); + + imx_hdp_state_init(hdp); + + sciErr = sc_ipc_getMuID(&hdp->mu_id); + if (sciErr != SC_ERR_NONE) { + pr_err("Cannot obtain MU ID\n"); + return -EINVAL; + } + + sciErr = sc_ipc_open(&hdp->ipcHndl, hdp->mu_id); + if (sciErr != SC_ERR_NONE) { + pr_err("sc_ipc_open failed! (sciError = %d)\n", sciErr); + return -EINVAL; + } + + hdp->link_rate = AFE_LINK_RATE_1_6; + + hdp->dual_mode = false; + + dp_pixel_link_config(hdp); + dp_clock_init(hdp); + + hdp_ipg_clock_set_rate(hdp); + + dp_ipg_clock_enable(hdp); + + /* Pixel Format - 1 RGB, 2 YCbCr 444, 3 YCbCr 420 */ + /* bpp (bits per subpixel) - 8 24bpp, 10 30bpp, 12 36bpp, 16 48bpp */ + hdp_phy_reset(0); + + imx_hdp_call(hdp, fw_load, &hdp->state); + core_rate = clk_get_rate(hdp->clks.clk_core); + + imx_hdp_call(hdp, fw_init, &hdp->state, core_rate); + if (hdp->is_hdmi == true) + /* default set hdmi to 1080p60 mode */ + imx_hdp_call(hdp, phy_init, &hdp->state, 2, 1, 8); + else + imx_hdp_call(hdp, phy_init, &hdp->state, 4, hdp->link_rate, 0); + +#ifdef hdp_irq + ret = devm_request_threaded_irq(dev, irq, + NULL, imx_hdp_irq_handler, + IRQF_IRQPOLL, dev_name(dev), dp); + if (ret) { + dev_err(&pdev->dev, "can't claim irq %d\n", irq); + goto err_irq; + } +#else + hpd_worker = kthread_create(hpd_det_worker, hdp, "hdp-hpd"); + if (IS_ERR(hpd_worker)) { + printk("failed create hpd thread\n"); + } + + wake_up_process(hpd_worker); /* avoid contributing to loadavg */ +#endif + + return 0; +#ifdef hdp_irq +err_irq: + drm_encoder_cleanup(encoder); + return ret; +#endif +} + +static void imx_hdp_imx_unbind(struct device *dev, struct device *master, + void *data) +{ + struct imx_hdp *hdp = dev_get_drvdata(dev); + + imx_hdp_deinit(hdp); + sc_ipc_close(hdp->mu_id); + return; +} + +static const struct component_ops imx_hdp_imx_ops = { + .bind = imx_hdp_imx_bind, + .unbind = imx_hdp_imx_unbind, +}; + +static int imx_hdp_imx_probe(struct platform_device *pdev) +{ + return component_add(&pdev->dev, &imx_hdp_imx_ops); +} + +static int imx_hdp_imx_remove(struct platform_device *pdev) +{ + component_del(&pdev->dev, &imx_hdp_imx_ops); + + return 0; +} + +static struct platform_driver imx_hdp_imx_platform_driver = { + .probe = imx_hdp_imx_probe, + .remove = imx_hdp_imx_remove, + .driver = { + .name = "i.mx8-hdp", + .of_match_table = imx_hdp_dt_ids, + }, +}; + +module_platform_driver(imx_hdp_imx_platform_driver); + +MODULE_AUTHOR("Sandor Yu <Sandor.yu@nxp.com>"); +MODULE_DESCRIPTION("IMX8QM DP Display Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dp-hdmi-imx"); diff --git a/drivers/gpu/drm/imx/hdp/imx-hdp.h b/drivers/gpu/drm/imx/hdp/imx-hdp.h new file mode 100644 index 00000000000000..de4baa4cda0f04 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/imx-hdp.h @@ -0,0 +1,193 @@ +/* + * Copyright 2017 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * 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. + */ +#ifndef _IMX_HDP_H_ +#define _IMX_HDP_H_ + +#include <linux/regmap.h> +#include <linux/mutex.h> +#include <drm/drm_of.h> +#include <drm/drmP.h> +#include <drm/drm_atomic_helper.h> +#include <drm/drm_crtc_helper.h> +#include <drm/drm_edid.h> +#include <drm/drm_encoder_slave.h> +#include <soc/imx8/sc/sci.h> + +#include <drm/drm_dp_helper.h> +#include "../../../../mxc/hdp/all.h" + +#define PLL_1188MHZ (1188000000) +#define PLL_675MHZ (675000000) + +#define HDP_TX_SS_LIS_BASE 0x0000 +#define HDP_TX_SS_CSR_BASE 0x1000 +#define HDP_TX_SS_GPIO_BASE 0x2000 +#define HDP_TX_SS_CTRL0_BASE 0x8000 + +#define CSR_PIXEL_LINK_MUX_CTL 0x00 +#define PL_MUX_CTL_VCP_OFFSET 5 +#define PL_MUX_CTL_HCP_OFFSET 4 +#define PL_MUX_CTL_PL_MUX_OFFSET 2 +#define PL_MUX_CTL_PL_SEL_OFFSET 0 + +#define CSR_PIXEL_LINK_MUX_STATUS 0x04 +#define PL_MUX_STATUS_PL1_INT_OFFSET 18 +#define PL_MUX_STATUS_PL1_ADD_OFFSET 16 +#define PL_MUX_STATUS_PL1_TYP_OFFSET 11 +#define PL_MUX_STATUS_PL0_INT_OFFSET 9 +#define PL_MUX_STATUS_PL0_ADD_OFFSET 7 +#define PL_MUX_STATUS_PL0_TYP_OFFSET 2 +#define PL_MUX_STATUS_PL_DLY_OFFSET 0 + +#define CSR_HDP_TX_CTRL_CTRL0 0x08 +#define CSR_HDP_TX_CTRL_CTRL1 0x0c + +/** + * imx_hdp_call - Calls a struct imx hdp_operations operation on + * an entity + * + * @entity: entity where the @operation will be called + * @operation: type of the operation. Should be the name of a member of + * struct &media_entity_operations. + * + * This helper function will check if @operation is not %NULL. On such case, + * it will issue a call to @operation\(@args\). + */ + +#define imx_hdp_call(hdp, operation, args...) \ + (!(hdp) ? -ENODEV : (((hdp)->ops && (hdp)->ops->operation) ? \ + (hdp)->ops->operation(args) : -ENOIOCTLCMD)) + +struct hdp_ops { + void (*fw_load)(state_struct *state); + void (*fw_init)(state_struct *state, u32 rate); + void (*phy_init)(state_struct *state, int vic, int format, int color_depth); + void (*mode_set)(state_struct *state, int vic, int format, int color_depth, int max_link); + int (*get_edid_block)(void *data, u8 *buf, u32 block, size_t len); + void (*get_hpd_state)(state_struct *state, u8 *hpd); +}; + +struct hdp_devtype { + u8 load_fw; + u8 is_hdmi; + struct hdp_ops *ops; + struct hdp_rw_func *rw; +}; + +struct hdp_video { + u32 bpp; + u32 format; + u32 lanes; + u32 color_type; /* bt */ + u32 color_depth; /* bpc */ + struct drm_display_mode cur_mode; + struct drm_display_mode pre_mode; + void __iomem *regs_base; +}; + +struct hdp_audio { + u32 interface; /* I2S SPDIF */ + u32 freq; + u32 nlanes; + u32 nChannels; + u32 sample_width; + u32 sample_rate; + u32 audio_cts; + u32 audio_n; + bool audio_enable; + spinlock_t audio_lock; + struct mutex audio_mutex; + void __iomem *regs_base; +}; + +struct hdp_hdcp { + void __iomem *regs_base; +}; + +struct hdp_phy { + u32 index; + u32 number; + bool enabled; + struct phy *phy; + void __iomem *regs_base; +}; + +struct hdp_clks { + struct clk *av_pll; + struct clk *dig_pll; + struct clk *clk_ipg; + struct clk *clk_core; + struct clk *clk_pxl; + struct clk *clk_pxl_mux; + struct clk *clk_pxl_link; + + struct clk *clk_hdp; + struct clk *clk_phy; + struct clk *clk_apb; + + struct clk *clk_lis; + struct clk *clk_msi; + struct clk *clk_lpcg; + struct clk *clk_even; + struct clk *clk_dbl; + struct clk *clk_vif; + struct clk *clk_apb_csr; + struct clk *clk_apb_ctrl; + struct clk *av_pll_div; + struct clk *dig_pll_div; +}; + +struct imx_hdp { + struct device *dev; + struct drm_connector connector; + struct drm_encoder encoder; + struct drm_bridge bridge; + + struct edid *edid; + char cable_state; + char fw_running; + + void __iomem *regs_base; /* Controller regs base */ + void __iomem *ss_base; /* HDP Subsystem regs base */ + + u8 load_fw; + u8 is_hdmi; + + struct mutex mutex; /* for state below and previous_mode */ + enum drm_connector_force force; /* mutex-protected force state */ + + struct hdp_video video; + + struct drm_dp_aux aux; + struct mutex aux_mutex; + + struct drm_dp_link dp_link; + S_LINK_STAT lkstat; + ENUM_AFE_LINK_RATE link_rate; + + sc_ipc_t ipcHndl; + u32 mu_id; + u32 dual_mode; + struct hdp_ops *ops; + struct hdp_rw_func *rw; + struct hdp_clks clks; + state_struct state; + +}; + +int imx_hdpaux_init(struct device *dev, struct imx_hdp *dp); +void imx_hdpaux_destroy(struct device *dev, struct imx_hdp *dp); +void hdp_phy_reset(u8 reset); + +#endif diff --git a/drivers/gpu/drm/imx/hdp/mhdp_firmware.h b/drivers/gpu/drm/imx/hdp/mhdp_firmware.h new file mode 100644 index 00000000000000..c52c8e30291287 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/mhdp_firmware.h @@ -0,0 +1,77 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * This file was auto-generated from dram0.data and iram0.data firmware images + * Do not edit it manually. + * + ****************************************************************************** + * + * mhdp_firmware.c + * + ****************************************************************************** + */ +#include <linux/io.h> + +static u32 const mhdp_iram0[] = { + 0x00000000, +}; + +static u32 const mhdp_dram0[] = { + 0x00000000, +}; + +u32 const *mhdp_iram0_get_ptr(void) +{ + return (u32 const *)mhdp_iram0; +} + +u32 const *mhdp_dram0_get_ptr(void) +{ + return (u32 const *)mhdp_dram0; +} + +size_t mhdp_iram0_get_size(void) +{ + return sizeof(mhdp_iram0); +} + +size_t mhdp_dram0_get_size(void) +{ + return sizeof(mhdp_dram0); +} diff --git a/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.c b/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.c new file mode 100644 index 00000000000000..1048ec206a0779 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.c @@ -0,0 +1,106 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * ss28fdsoi_hdmitx_table.c + * + ****************************************************************************** + */ + +#include "ss28fdsoi_hdmitx_table.h" + +const u32 ss28fdsoi_hdmitx_clock_control_table[SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_ROWS][SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_COLS] = { + { 25000, 42500, 1000, 250000, 425000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 2000000, 3400000, 0, 2, 2, 2, 4, 125000, 212500, 0x03, 25000, 42500 }, + { 42500, 85000, 1000, 425000, 850000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 4, 212500, 425000, 0x02, 42500, 85000 }, + { 85000, 170000, 1000, 850000, 1700000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 2, 425000, 850000, 0x01, 85000, 170000 }, + { 170000, 340000, 1000, 1700000, 3400000, 0x22, 0x01, 0x07, 340, 0x146, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 1, 850000, 1700000, 0x00, 170000, 340000 }, + { 340000, 600000, 1000, 3400000, 6000000, 0x3C, 0x03, 0x06, 600, 0x24A, 0x00A, 3400000, 6000000, 1, 1, 1, 2, 1, 1700000, 3000000, 0x00, 340000, 600000 }, + { 25000, 34000, 1205, 312500, 425000, 0x04, 0x01, 0x01, 400, 0x182, 0x00A, 2500000, 3400000, 0, 2, 2, 2, 4, 156250, 212500, 0x03, 31250, 42500 }, + { 34000, 68000, 1205, 425000, 850000, 0x06, 0x02, 0x01, 300, 0x11E, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 4, 212500, 425000, 0x02, 42500, 85000 }, + { 68000, 136000, 1205, 850000, 1700000, 0x0D, 0x02, 0x02, 325, 0x137, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 2, 425000, 850000, 0x01, 85000, 170000 }, + { 136000, 272000, 1205, 1700000, 3400000, 0x1A, 0x02, 0x04, 325, 0x137, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 1, 850000, 1700000, 0x00, 170000, 340000 }, + { 272000, 480000, 1205, 3400000, 6000000, 0x30, 0x03, 0x05, 600, 0x24A, 0x00A, 3400000, 6000000, 1, 1, 1, 2, 1, 1700000, 3000000, 0x00, 340000, 600000 }, + { 25000, 28000, 1500, 375000, 420000, 0x03, 0x01, 0x01, 360, 0x15A, 0x00A, 3000000, 3360000, 0, 2, 2, 2, 4, 187500, 210000, 0x03, 37500, 42000 }, + { 28000, 56000, 1500, 420000, 840000, 0x06, 0x02, 0x01, 360, 0x15A, 0x00A, 1680000, 3360000, 0, 1, 1, 2, 4, 210000, 420000, 0x02, 42000, 84000 }, + { 56000, 113000, 1500, 840000, 1695000, 0x0B, 0x00, 0x05, 330, 0x13C, 0x00A, 1680000, 3390000, 0, 1, 1, 2, 2, 420000, 847500, 0x01, 84000, 169500 }, + { 113000, 226000, 1500, 1695000, 3390000, 0x16, 0x01, 0x05, 330, 0x13C, 0x00A, 1695000, 3390000, 0, 1, 1, 2, 1, 847500, 1695000, 0x00, 169500, 339000 }, + { 226000, 400000, 1500, 3390000, 6000000, 0x28, 0x03, 0x04, 600, 0x24A, 0x00A, 3390000, 6000000, 1, 1, 1, 2, 1, 1695000, 3000000, 0x00, 339000, 600000 }, + { 25000, 42500, 2000, 500000, 850000, 0x05, 0x01, 0x01, 400, 0x182, 0x00A, 2000000, 3400000, 0, 1, 1, 2, 4, 250000, 425000, 0x02, 50000, 85000 }, + { 42500, 85000, 2000, 850000, 1700000, 0x08, 0x03, 0x01, 320, 0x132, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 2, 425000, 850000, 0x01, 85000, 170000 }, + { 85000, 170000, 2000, 1700000, 3400000, 0x11, 0x00, 0x07, 340, 0x146, 0x00A, 1700000, 3400000, 0, 1, 1, 2, 1, 850000, 1700000, 0x00, 170000, 340000 }, + { 170000, 300000, 2000, 3400000, 6000000, 0x22, 0x01, 0x06, 680, 0x29A, 0x00A, 3400000, 6000000, 1, 1, 1, 2, 1, 1700000, 3000000, 0x00, 340000, 600000 } +}; + +const u32 ss28fdsoi_hdmitx_pll_tuning_table[SS28FDSOI_HDMITX_PLL_TUNING_TABLE_ROWS][SS28FDSOI_HDMITX_PLL_TUNING_TABLE_COLS] = { + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300.0, 0x08D}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320.0, 0x08E}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325.0, 0x08E}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330.0, 0x08E}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340.0, 0x08F}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360.0, 0x0A7}, + {0, 1700000, 2000000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400.0, 0x0C5}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 300.0, 0x086}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 320.0, 0x087}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 325.0, 0x087}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 330.0, 0x104}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 340.0, 0x08B}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 360.0, 0x08D}, + {1, 2000000, 2400000, 0x3, 0x1, 0x0, 0x8C, 0x2E, 400.0, 0x0A6}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300.0, 0x04E}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320.0, 0x04F}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325.0, 0x04F}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330.0, 0x085}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340.0, 0x085}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360.0, 0x086}, + {2, 2400000, 2800000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400.0, 0x08B}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 300.0, 0x047}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 320.0, 0x04B}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 325.0, 0x04B}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 330.0, 0x04B}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 340.0, 0x04D}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 360.0, 0x04E}, + {3, 2800000, 3400000, 0x3, 0x1, 0x0, 0x04, 0x0D, 400.0, 0x085}, + {4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600.0, 0x08D}, + {4, 3400000, 3900000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680.0, 0x0A6}, + {5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 600.0, 0x087}, + {5, 3900000, 4500000, 0x7, 0x1, 0x0, 0x8E, 0x2F, 680.0, 0x0A4}, + {6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600.0, 0x04F}, + {6, 4500000, 5200000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680.0, 0x086}, + {7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 600.0, 0x04D}, + {7, 5200000, 6000000, 0x7, 0x1, 0x0, 0x04, 0x0D, 680.0, 0x04F} +}; diff --git a/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.h b/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.h new file mode 100644 index 00000000000000..7bd48fc8726863 --- /dev/null +++ b/drivers/gpu/drm/imx/hdp/ss28fdsoi_hdmitx_table.h @@ -0,0 +1,109 @@ +/****************************************************************************** + * + * Copyright (C) 2016-2017 Cadence Design Systems, Inc. + * All rights reserved worldwide. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. 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. + * + * 3. Neither the name of the copyright holder 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 HOLDER 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. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + ****************************************************************************** + * + * ss28fdsoi_hdmitx_table.h + * + ****************************************************************************** + */ + +#ifndef SS28FDSOI_HDMITX_TABLE_H_ +#define SS28FDSOI_HDMITX_TABLE_H_ + +#include <linux/io.h> + +# define SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_ROWS 19 +# define SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_COLS 23 + +# define SS28FDSOI_HDMITX_PLL_TUNING_TABLE_ROWS 36 +# define SS28FDSOI_HDMITX_PLL_TUNING_TABLE_COLS 19 + +typedef enum { + //PIXEL_CLK_FREQ_MHZ_MIN, + PIXEL_CLK_FREQ_KHZ_MIN, + //PIXEL_CLK_FREQ_MHZ_MAX, + PIXEL_CLK_FREQ_KHZ_MAX, + FEEDBACK_FACTOR, + DATA_RANGE_MBPS_MIN, + DATA_RANGE_MBPS_MAX, + CMNDA_PLL0_IP_DIV, + CMN_REF_CLK_DIG_DIV, + REF_CLK_DIVIDER_SCALER, + PLL_FB_DIV_TOTAL, + CMNDA_PLL0_FB_DIV_LOW, + CMNDA_PLL0_FB_DIV_HIGH, + //VCO_FREQ_MHZ_MIN, + //VCO_FREQ_MHZ_MAX, + VCO_FREQ_KHZ_MIN, + VCO_FREQ_KHZ_MAX, + VCO_RING_SELECT, + CMNDA_HS_CLK_0_SEL, + CMNDA_HS_CLK_1_SEL, + HSCLK_DIV_AT_XCVR, + HSCLK_DIV_TX_SUB_RATE, + TX_CLK_KHZ_MIN, + TX_CLK_KHZ_MAX, + CMNDA_PLL0_HS_SYM_DIV_SEL, + CMNDA_PLL0_CLK_FREQ_KHZ_MIN, + CMNDA_PLL0_CLK_FREQ_KHZ_MAX +} CLK_CTRL_PARAM; + +typedef enum { + VCO_FREQ_BIN, + /* PLL_VCO_FREQ_MHZ_MIN, */ + /* PLL_VCO_FREQ_MHZ_MAX, */ + PLL_VCO_FREQ_KHZ_MIN, + PLL_VCO_FREQ_KHZ_MAX, + VOLTAGE_TO_CURRENT_COARSE, + VOLTAGE_TO_CURRENT, + NDAC_CTRL, + PMOS_CTRL, + PTAT_NDAC_CTRL, + /*PLL_FEEDBACK_DIV_TOTAL, float to int */ + PLL_FEEDBACK_DIV_TOTAL, + CHARGE_PUMP_GAIN +} PLL_TUNE_PARAM; + +extern const u32 +ss28fdsoi_hdmitx_clock_control_table[SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_ROWS] + [SS28FDSOI_HDMITX_CLOCK_CONTROL_TABLE_COLS]; +extern const u32 +ss28fdsoi_hdmitx_pll_tuning_table[SS28FDSOI_HDMITX_PLL_TUNING_TABLE_ROWS] + [SS28FDSOI_HDMITX_PLL_TUNING_TABLE_COLS]; + +#endif -- GitLab