145810d48SGuillaume Ranquet // SPDX-License-Identifier: GPL-2.0 245810d48SGuillaume Ranquet /* 345810d48SGuillaume Ranquet * Copyright (c) 2022 MediaTek Inc. 445810d48SGuillaume Ranquet * Copyright (c) 2022 BayLibre, SAS 545810d48SGuillaume Ranquet */ 645810d48SGuillaume Ranquet #include <linux/delay.h> 745810d48SGuillaume Ranquet #include <linux/io.h> 845810d48SGuillaume Ranquet #include <linux/mfd/syscon.h> 945810d48SGuillaume Ranquet #include <linux/module.h> 1045810d48SGuillaume Ranquet #include <linux/phy/phy.h> 1145810d48SGuillaume Ranquet #include <linux/platform_device.h> 1245810d48SGuillaume Ranquet #include <linux/types.h> 1345810d48SGuillaume Ranquet #include <linux/units.h> 1445810d48SGuillaume Ranquet #include <linux/nvmem-consumer.h> 1545810d48SGuillaume Ranquet 1645810d48SGuillaume Ranquet #include "phy-mtk-io.h" 1745810d48SGuillaume Ranquet #include "phy-mtk-hdmi.h" 1845810d48SGuillaume Ranquet #include "phy-mtk-hdmi-mt8195.h" 1945810d48SGuillaume Ranquet 2045810d48SGuillaume Ranquet static void mtk_hdmi_ana_fifo_en(struct mtk_hdmi_phy *hdmi_phy) 2145810d48SGuillaume Ranquet { 2245810d48SGuillaume Ranquet /* make data fifo writable for hdmi2.0 */ 2345810d48SGuillaume Ranquet mtk_phy_set_bits(hdmi_phy->regs + HDMI_ANA_CTL, REG_ANA_HDMI20_FIFO_EN); 2445810d48SGuillaume Ranquet } 2545810d48SGuillaume Ranquet 2645810d48SGuillaume Ranquet static void 2745810d48SGuillaume Ranquet mtk_phy_tmds_clk_ratio(struct mtk_hdmi_phy *hdmi_phy, bool enable) 2845810d48SGuillaume Ranquet { 2945810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 3045810d48SGuillaume Ranquet 3145810d48SGuillaume Ranquet mtk_hdmi_ana_fifo_en(hdmi_phy); 3245810d48SGuillaume Ranquet 3345810d48SGuillaume Ranquet /* HDMI 2.0 specification, 3.4Gbps <= TMDS Bit Rate <= 6G, 3445810d48SGuillaume Ranquet * clock bit ratio 1:40, under 3.4Gbps, clock bit ratio 1:10 3545810d48SGuillaume Ranquet */ 3645810d48SGuillaume Ranquet if (enable) 3745810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI20_CLK_CFG, REG_TXC_DIV, 3); 3845810d48SGuillaume Ranquet else 3945810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI20_CLK_CFG, REG_TXC_DIV); 4045810d48SGuillaume Ranquet } 4145810d48SGuillaume Ranquet 4245810d48SGuillaume Ranquet static void mtk_hdmi_pll_sel_src(struct clk_hw *hw) 4345810d48SGuillaume Ranquet { 4445810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 4545810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 4645810d48SGuillaume Ranquet 4745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_CTL_3, REG_HDMITX_REF_XTAL_SEL); 4845810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_CTL_3, REG_HDMITX_REF_RESPLL_SEL); 4945810d48SGuillaume Ranquet 5045810d48SGuillaume Ranquet /* DA_HDMITX21_REF_CK for TXPLL input source */ 5145810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_10, RG_HDMITXPLL_REF_CK_SEL); 5245810d48SGuillaume Ranquet } 5345810d48SGuillaume Ranquet 5445810d48SGuillaume Ranquet static void mtk_hdmi_pll_perf(struct clk_hw *hw) 5545810d48SGuillaume Ranquet { 5645810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 5745810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 5845810d48SGuillaume Ranquet 5945810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_0, RG_HDMITXPLL_BP2); 6045810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_BC); 6145810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_IC, 0x1); 6245810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_BR, 0x2); 6345810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_IR, 0x2); 6445810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_BP); 6545810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_0, RG_HDMITXPLL_IBAND_FIX_EN); 6645810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT14); 6745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_HIKVCO); 6845810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_0, RG_HDMITXPLL_HREN, 0x1); 6945810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_0, RG_HDMITXPLL_LVR_SEL, 0x1); 7045810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT12_11); 7145810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_0, RG_HDMITXPLL_TCL_EN); 7245810d48SGuillaume Ranquet } 7345810d48SGuillaume Ranquet 7445810d48SGuillaume Ranquet static int mtk_hdmi_pll_set_hw(struct clk_hw *hw, u8 prediv, 7545810d48SGuillaume Ranquet u8 fbkdiv_high, 7645810d48SGuillaume Ranquet u32 fbkdiv_low, 7745810d48SGuillaume Ranquet u8 fbkdiv_hs3, u8 posdiv1, 7845810d48SGuillaume Ranquet u8 posdiv2, u8 txprediv, 7945810d48SGuillaume Ranquet u8 txposdiv, 8045810d48SGuillaume Ranquet u8 digital_div) 8145810d48SGuillaume Ranquet { 8245810d48SGuillaume Ranquet u8 txposdiv_value; 8345810d48SGuillaume Ranquet u8 div3_ctrl_value; 8445810d48SGuillaume Ranquet u8 posdiv_vallue; 8545810d48SGuillaume Ranquet u8 div_ctrl_value; 8645810d48SGuillaume Ranquet u8 reserve_3_2_value; 8745810d48SGuillaume Ranquet u8 prediv_value; 8845810d48SGuillaume Ranquet u8 reserve13_value; 8945810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 9045810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 9145810d48SGuillaume Ranquet 9245810d48SGuillaume Ranquet mtk_hdmi_pll_sel_src(hw); 9345810d48SGuillaume Ranquet 9445810d48SGuillaume Ranquet mtk_hdmi_pll_perf(hw); 9545810d48SGuillaume Ranquet 9645810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_10, RG_HDMITX21_BIAS_PE_BG_VREF_SEL, 0x2); 9745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_10, RG_HDMITX21_VREF_SEL); 9845810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_9, RG_HDMITX21_SLDO_VREF_SEL, 0x2); 9945810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_10, RG_HDMITX21_BIAS_PE_VREF_SELB); 10045810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_3, RG_HDMITX21_SLDOLPF_EN); 10145810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_6, RG_HDMITX21_INTR_CAL, 0x11); 10245810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_PWD); 10345810d48SGuillaume Ranquet 10445810d48SGuillaume Ranquet /* TXPOSDIV */ 10545810d48SGuillaume Ranquet txposdiv_value = ilog2(txposdiv); 10645810d48SGuillaume Ranquet 10745810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_6, RG_HDMITX21_TX_POSDIV, txposdiv_value); 10845810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_TX_POSDIV_EN); 10945810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_FRL_EN); 11045810d48SGuillaume Ranquet 11145810d48SGuillaume Ranquet /* TXPREDIV */ 11245810d48SGuillaume Ranquet switch (txprediv) { 11345810d48SGuillaume Ranquet case 2: 11445810d48SGuillaume Ranquet div3_ctrl_value = 0x0; 11545810d48SGuillaume Ranquet posdiv_vallue = 0x0; 11645810d48SGuillaume Ranquet break; 11745810d48SGuillaume Ranquet case 4: 11845810d48SGuillaume Ranquet div3_ctrl_value = 0x0; 11945810d48SGuillaume Ranquet posdiv_vallue = 0x1; 12045810d48SGuillaume Ranquet break; 12145810d48SGuillaume Ranquet case 6: 12245810d48SGuillaume Ranquet div3_ctrl_value = 0x1; 12345810d48SGuillaume Ranquet posdiv_vallue = 0x0; 12445810d48SGuillaume Ranquet break; 12545810d48SGuillaume Ranquet case 12: 12645810d48SGuillaume Ranquet div3_ctrl_value = 0x1; 12745810d48SGuillaume Ranquet posdiv_vallue = 0x1; 12845810d48SGuillaume Ranquet break; 12945810d48SGuillaume Ranquet default: 13045810d48SGuillaume Ranquet return -EINVAL; 13145810d48SGuillaume Ranquet } 13245810d48SGuillaume Ranquet 13345810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_4, RG_HDMITXPLL_POSDIV_DIV3_CTRL, div3_ctrl_value); 13445810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_4, RG_HDMITXPLL_POSDIV, posdiv_vallue); 13545810d48SGuillaume Ranquet 13645810d48SGuillaume Ranquet /* POSDIV1 */ 13745810d48SGuillaume Ranquet switch (posdiv1) { 13845810d48SGuillaume Ranquet case 5: 13945810d48SGuillaume Ranquet div_ctrl_value = 0x0; 14045810d48SGuillaume Ranquet break; 14145810d48SGuillaume Ranquet case 10: 14245810d48SGuillaume Ranquet div_ctrl_value = 0x1; 14345810d48SGuillaume Ranquet break; 14445810d48SGuillaume Ranquet case 12: 14545810d48SGuillaume Ranquet div_ctrl_value = 0x2; 14645810d48SGuillaume Ranquet break; 14745810d48SGuillaume Ranquet case 15: 14845810d48SGuillaume Ranquet div_ctrl_value = 0x3; 14945810d48SGuillaume Ranquet break; 15045810d48SGuillaume Ranquet default: 15145810d48SGuillaume Ranquet return -EINVAL; 15245810d48SGuillaume Ranquet } 15345810d48SGuillaume Ranquet 15445810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_4, RG_HDMITXPLL_DIV_CTRL, div_ctrl_value); 15545810d48SGuillaume Ranquet 15645810d48SGuillaume Ranquet /* DE add new setting */ 15745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT14); 15845810d48SGuillaume Ranquet 15945810d48SGuillaume Ranquet /* POSDIV2 */ 16045810d48SGuillaume Ranquet switch (posdiv2) { 16145810d48SGuillaume Ranquet case 1: 16245810d48SGuillaume Ranquet reserve_3_2_value = 0x0; 16345810d48SGuillaume Ranquet break; 16445810d48SGuillaume Ranquet case 2: 16545810d48SGuillaume Ranquet reserve_3_2_value = 0x1; 16645810d48SGuillaume Ranquet break; 16745810d48SGuillaume Ranquet case 4: 16845810d48SGuillaume Ranquet reserve_3_2_value = 0x2; 16945810d48SGuillaume Ranquet break; 17045810d48SGuillaume Ranquet case 6: 17145810d48SGuillaume Ranquet reserve_3_2_value = 0x3; 17245810d48SGuillaume Ranquet break; 17345810d48SGuillaume Ranquet default: 17445810d48SGuillaume Ranquet return -EINVAL; 17545810d48SGuillaume Ranquet } 17645810d48SGuillaume Ranquet 17745810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT3_2, reserve_3_2_value); 17845810d48SGuillaume Ranquet 17945810d48SGuillaume Ranquet /* DE add new setting */ 18045810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT1_0, 0x2); 18145810d48SGuillaume Ranquet 18245810d48SGuillaume Ranquet /* PREDIV */ 18345810d48SGuillaume Ranquet prediv_value = ilog2(prediv); 18445810d48SGuillaume Ranquet 18545810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_4, RG_HDMITXPLL_PREDIV, prediv_value); 18645810d48SGuillaume Ranquet 18745810d48SGuillaume Ranquet /* FBKDIV_HS3 */ 18845810d48SGuillaume Ranquet reserve13_value = ilog2(fbkdiv_hs3); 18945810d48SGuillaume Ranquet 19045810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_1, RG_HDMITXPLL_RESERVE_BIT13, reserve13_value); 19145810d48SGuillaume Ranquet 19245810d48SGuillaume Ranquet /* FBDIV */ 19345810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_4, RG_HDMITXPLL_FBKDIV_HIGH, fbkdiv_high); 19445810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_PLL_CFG_3, RG_HDMITXPLL_FBKDIV_LOW, fbkdiv_low); 19545810d48SGuillaume Ranquet 19645810d48SGuillaume Ranquet /* Digital DIVIDER */ 19745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_CTL_3, REG_PIXEL_CLOCK_SEL); 19845810d48SGuillaume Ranquet 19945810d48SGuillaume Ranquet if (digital_div == 1) { 20045810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_CTL_3, REG_HDMITX_PIXEL_CLOCK); 20145810d48SGuillaume Ranquet } else { 20245810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_CTL_3, REG_HDMITX_PIXEL_CLOCK); 20345810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_CTL_3, REG_HDMITXPLL_DIV, digital_div - 1); 20445810d48SGuillaume Ranquet } 20545810d48SGuillaume Ranquet 20645810d48SGuillaume Ranquet return 0; 20745810d48SGuillaume Ranquet } 20845810d48SGuillaume Ranquet 20945810d48SGuillaume Ranquet static int mtk_hdmi_pll_calc(struct mtk_hdmi_phy *hdmi_phy, struct clk_hw *hw, 21045810d48SGuillaume Ranquet unsigned long rate, unsigned long parent_rate) 21145810d48SGuillaume Ranquet { 21245810d48SGuillaume Ranquet u8 digital_div, txprediv, txposdiv, fbkdiv_high, posdiv1, posdiv2; 21345810d48SGuillaume Ranquet u64 tmds_clk, pixel_clk, da_hdmitx21_ref_ck, ns_hdmipll_ck, pcw; 21445810d48SGuillaume Ranquet u8 txpredivs[4] = { 2, 4, 6, 12 }; 21545810d48SGuillaume Ranquet u32 fbkdiv_low; 216714dd3c2SGuillaume Ranquet int i; 21745810d48SGuillaume Ranquet 21845810d48SGuillaume Ranquet pixel_clk = rate; 21945810d48SGuillaume Ranquet tmds_clk = pixel_clk; 22045810d48SGuillaume Ranquet 22145810d48SGuillaume Ranquet if (tmds_clk < 25 * MEGA || tmds_clk > 594 * MEGA) 22245810d48SGuillaume Ranquet return -EINVAL; 22345810d48SGuillaume Ranquet 22445810d48SGuillaume Ranquet if (tmds_clk >= 340 * MEGA) 22545810d48SGuillaume Ranquet hdmi_phy->tmds_over_340M = true; 22645810d48SGuillaume Ranquet else 22745810d48SGuillaume Ranquet hdmi_phy->tmds_over_340M = false; 22845810d48SGuillaume Ranquet 22945810d48SGuillaume Ranquet /* in Hz */ 23045810d48SGuillaume Ranquet da_hdmitx21_ref_ck = 26 * MEGA; 23145810d48SGuillaume Ranquet 23245810d48SGuillaume Ranquet /* TXPOSDIV stage treatment: 23345810d48SGuillaume Ranquet * 0M < TMDS clk < 54M /8 23445810d48SGuillaume Ranquet * 54M <= TMDS clk < 148.35M /4 23545810d48SGuillaume Ranquet * 148.35M <=TMDS clk < 296.7M /2 23645810d48SGuillaume Ranquet * 296.7 <=TMDS clk <= 594M /1 23745810d48SGuillaume Ranquet */ 23845810d48SGuillaume Ranquet if (tmds_clk < 54 * MEGA) 23945810d48SGuillaume Ranquet txposdiv = 8; 24045810d48SGuillaume Ranquet else if (tmds_clk >= 54 * MEGA && tmds_clk < 148.35 * MEGA) 24145810d48SGuillaume Ranquet txposdiv = 4; 24245810d48SGuillaume Ranquet else if (tmds_clk >= 148.35 * MEGA && tmds_clk < 296.7 * MEGA) 24345810d48SGuillaume Ranquet txposdiv = 2; 24445810d48SGuillaume Ranquet else if (tmds_clk >= 296.7 * MEGA && tmds_clk <= 594 * MEGA) 24545810d48SGuillaume Ranquet txposdiv = 1; 24645810d48SGuillaume Ranquet else 24745810d48SGuillaume Ranquet return -EINVAL; 24845810d48SGuillaume Ranquet 24945810d48SGuillaume Ranquet /* calculate txprediv: can be 2, 4, 6, 12 25045810d48SGuillaume Ranquet * ICO clk = 5*TMDS_CLK*TXPOSDIV*TXPREDIV 25145810d48SGuillaume Ranquet * ICO clk constraint: 5G =< ICO clk <= 12G 25245810d48SGuillaume Ranquet */ 25345810d48SGuillaume Ranquet for (i = 0; i < ARRAY_SIZE(txpredivs); i++) { 25445810d48SGuillaume Ranquet ns_hdmipll_ck = 5 * tmds_clk * txposdiv * txpredivs[i]; 25545810d48SGuillaume Ranquet if (ns_hdmipll_ck >= 5 * GIGA && 25645810d48SGuillaume Ranquet ns_hdmipll_ck <= 1 * GIGA) 25745810d48SGuillaume Ranquet break; 25845810d48SGuillaume Ranquet } 25945810d48SGuillaume Ranquet if (i == (ARRAY_SIZE(txpredivs) - 1) && 26045810d48SGuillaume Ranquet (ns_hdmipll_ck < 5 * GIGA || ns_hdmipll_ck > 12 * GIGA)) { 26145810d48SGuillaume Ranquet return -EINVAL; 26245810d48SGuillaume Ranquet } 26345810d48SGuillaume Ranquet if (i == ARRAY_SIZE(txpredivs)) 26445810d48SGuillaume Ranquet return -EINVAL; 26545810d48SGuillaume Ranquet 26645810d48SGuillaume Ranquet txprediv = txpredivs[i]; 26745810d48SGuillaume Ranquet 26845810d48SGuillaume Ranquet /* PCW calculation: FBKDIV 26945810d48SGuillaume Ranquet * formula: pcw=(frequency_out*2^pcw_bit) / frequency_in / FBKDIV_HS3; 27045810d48SGuillaume Ranquet * RG_HDMITXPLL_FBKDIV[32:0]: 27145810d48SGuillaume Ranquet * [32,24] 9bit integer, [23,0]:24bit fraction 27245810d48SGuillaume Ranquet */ 27345810d48SGuillaume Ranquet pcw = div_u64(((u64)ns_hdmipll_ck) << PCW_DECIMAL_WIDTH, 274*9d9ff3d2SGuillaume Ranquet da_hdmitx21_ref_ck * PLL_FBKDIV_HS3); 27545810d48SGuillaume Ranquet 27645810d48SGuillaume Ranquet if (pcw > GENMASK_ULL(32, 0)) 27745810d48SGuillaume Ranquet return -EINVAL; 27845810d48SGuillaume Ranquet 27945810d48SGuillaume Ranquet fbkdiv_high = FIELD_GET(GENMASK_ULL(63, 32), pcw); 28045810d48SGuillaume Ranquet fbkdiv_low = FIELD_GET(GENMASK(31, 0), pcw); 28145810d48SGuillaume Ranquet 28245810d48SGuillaume Ranquet /* posdiv1: 28345810d48SGuillaume Ranquet * posdiv1 stage treatment according to color_depth: 28445810d48SGuillaume Ranquet * 24bit -> posdiv1 /10, 30bit -> posdiv1 /12.5, 28545810d48SGuillaume Ranquet * 36bit -> posdiv1 /15, 48bit -> posdiv1 /10 28645810d48SGuillaume Ranquet */ 28745810d48SGuillaume Ranquet posdiv1 = 10; 28845810d48SGuillaume Ranquet posdiv2 = 1; 28945810d48SGuillaume Ranquet 29045810d48SGuillaume Ranquet /* Digital clk divider, max /32 */ 291*9d9ff3d2SGuillaume Ranquet digital_div = div_u64(ns_hdmipll_ck, posdiv1 * posdiv2 * pixel_clk); 29245810d48SGuillaume Ranquet if (!(digital_div <= 32 && digital_div >= 1)) 29345810d48SGuillaume Ranquet return -EINVAL; 29445810d48SGuillaume Ranquet 295714dd3c2SGuillaume Ranquet return mtk_hdmi_pll_set_hw(hw, PLL_PREDIV, fbkdiv_high, fbkdiv_low, 29645810d48SGuillaume Ranquet PLL_FBKDIV_HS3, posdiv1, posdiv2, txprediv, 29745810d48SGuillaume Ranquet txposdiv, digital_div); 29845810d48SGuillaume Ranquet } 29945810d48SGuillaume Ranquet 30045810d48SGuillaume Ranquet static int mtk_hdmi_pll_drv_setting(struct clk_hw *hw) 30145810d48SGuillaume Ranquet { 30245810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 30345810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 30445810d48SGuillaume Ranquet u8 data_channel_bias, clk_channel_bias; 30545810d48SGuillaume Ranquet u8 impedance, impedance_en; 30645810d48SGuillaume Ranquet u32 tmds_clk; 30745810d48SGuillaume Ranquet u32 pixel_clk = hdmi_phy->pll_rate; 30845810d48SGuillaume Ranquet 30945810d48SGuillaume Ranquet tmds_clk = pixel_clk; 31045810d48SGuillaume Ranquet 31145810d48SGuillaume Ranquet /* bias & impedance setting: 31245810d48SGuillaume Ranquet * 3G < data rate <= 6G: enable impedance 100ohm, 31345810d48SGuillaume Ranquet * data channel bias 24mA, clock channel bias 20mA 31445810d48SGuillaume Ranquet * pixel clk >= HD, 74.175MHZ <= pixel clk <= 300MHZ: 31545810d48SGuillaume Ranquet * enalbe impedance 100ohm 31645810d48SGuillaume Ranquet * data channel 20mA, clock channel 16mA 31745810d48SGuillaume Ranquet * 27M =< pixel clk < 74.175: disable impedance 31845810d48SGuillaume Ranquet * data channel & clock channel bias 10mA 31945810d48SGuillaume Ranquet */ 32045810d48SGuillaume Ranquet 32145810d48SGuillaume Ranquet /* 3G < data rate <= 6G, 300M < tmds rate <= 594M */ 32245810d48SGuillaume Ranquet if (tmds_clk > 300 * MEGA && tmds_clk <= 594 * MEGA) { 32345810d48SGuillaume Ranquet data_channel_bias = 0x3c; /* 24mA */ 32445810d48SGuillaume Ranquet clk_channel_bias = 0x34; /* 20mA */ 32545810d48SGuillaume Ranquet impedance_en = 0xf; 32645810d48SGuillaume Ranquet impedance = 0x36; /* 100ohm */ 32745810d48SGuillaume Ranquet } else if (pixel_clk >= 74.175 * MEGA && pixel_clk <= 300 * MEGA) { 32845810d48SGuillaume Ranquet data_channel_bias = 0x34; /* 20mA */ 32945810d48SGuillaume Ranquet clk_channel_bias = 0x2c; /* 16mA */ 33045810d48SGuillaume Ranquet impedance_en = 0xf; 33145810d48SGuillaume Ranquet impedance = 0x36; /* 100ohm */ 33245810d48SGuillaume Ranquet } else if (pixel_clk >= 27 * MEGA && pixel_clk < 74.175 * MEGA) { 33345810d48SGuillaume Ranquet data_channel_bias = 0x14; /* 10mA */ 33445810d48SGuillaume Ranquet clk_channel_bias = 0x14; /* 10mA */ 33545810d48SGuillaume Ranquet impedance_en = 0x0; 33645810d48SGuillaume Ranquet impedance = 0x0; 33745810d48SGuillaume Ranquet } else { 33845810d48SGuillaume Ranquet return -EINVAL; 33945810d48SGuillaume Ranquet } 34045810d48SGuillaume Ranquet 34145810d48SGuillaume Ranquet /* bias */ 34245810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_1, RG_HDMITX21_DRV_IBIAS_D0, data_channel_bias); 34345810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_1, RG_HDMITX21_DRV_IBIAS_D1, data_channel_bias); 34445810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_1, RG_HDMITX21_DRV_IBIAS_D2, data_channel_bias); 34545810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_0, RG_HDMITX21_DRV_IBIAS_CLK, clk_channel_bias); 34645810d48SGuillaume Ranquet 34745810d48SGuillaume Ranquet /* impedance */ 34845810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_0, RG_HDMITX21_DRV_IMP_EN, impedance_en); 34945810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_2, RG_HDMITX21_DRV_IMP_D0_EN1, impedance); 35045810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_2, RG_HDMITX21_DRV_IMP_D1_EN1, impedance); 35145810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_2, RG_HDMITX21_DRV_IMP_D2_EN1, impedance); 35245810d48SGuillaume Ranquet mtk_phy_update_field(regs + HDMI_1_CFG_2, RG_HDMITX21_DRV_IMP_CLK_EN1, impedance); 35345810d48SGuillaume Ranquet 35445810d48SGuillaume Ranquet return 0; 35545810d48SGuillaume Ranquet } 35645810d48SGuillaume Ranquet 35745810d48SGuillaume Ranquet static int mtk_hdmi_pll_prepare(struct clk_hw *hw) 35845810d48SGuillaume Ranquet { 35945810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 36045810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 36145810d48SGuillaume Ranquet 36245810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_TX_POSDIV_EN); 36345810d48SGuillaume Ranquet 36445810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_0, RG_HDMITX21_SER_EN); 36545810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_D0_DRV_OP_EN); 36645810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_D1_DRV_OP_EN); 36745810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_D2_DRV_OP_EN); 36845810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_CK_DRV_OP_EN); 36945810d48SGuillaume Ranquet 37045810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_FRL_D0_EN); 37145810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_FRL_D1_EN); 37245810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_FRL_D2_EN); 37345810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_FRL_CK_EN); 37445810d48SGuillaume Ranquet 37545810d48SGuillaume Ranquet mtk_hdmi_pll_drv_setting(hw); 37645810d48SGuillaume Ranquet 37745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_10, RG_HDMITX21_BG_PWD); 37845810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_BIAS_EN); 37945810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_3, RG_HDMITX21_CKLDO_EN); 38045810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_3, RG_HDMITX21_SLDO_EN); 38145810d48SGuillaume Ranquet 38245810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_4, DA_HDMITXPLL_PWR_ON); 38345810d48SGuillaume Ranquet usleep_range(5, 10); 38445810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_4, DA_HDMITXPLL_ISO_EN); 38545810d48SGuillaume Ranquet usleep_range(5, 10); 38645810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_PWD); 38745810d48SGuillaume Ranquet usleep_range(30, 50); 38845810d48SGuillaume Ranquet return 0; 38945810d48SGuillaume Ranquet } 39045810d48SGuillaume Ranquet 39145810d48SGuillaume Ranquet static void mtk_hdmi_pll_unprepare(struct clk_hw *hw) 39245810d48SGuillaume Ranquet { 39345810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 39445810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 39545810d48SGuillaume Ranquet 39645810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_10, RG_HDMITX21_BG_PWD); 39745810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_6, RG_HDMITX21_BIAS_EN); 39845810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_3, RG_HDMITX21_CKLDO_EN); 39945810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_3, RG_HDMITX21_SLDO_EN); 40045810d48SGuillaume Ranquet 40145810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_2, RG_HDMITXPLL_PWD); 40245810d48SGuillaume Ranquet usleep_range(10, 20); 40345810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_PLL_CFG_4, DA_HDMITXPLL_ISO_EN); 40445810d48SGuillaume Ranquet usleep_range(10, 20); 40545810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_PLL_CFG_4, DA_HDMITXPLL_PWR_ON); 40645810d48SGuillaume Ranquet } 40745810d48SGuillaume Ranquet 40845810d48SGuillaume Ranquet static int mtk_hdmi_pll_set_rate(struct clk_hw *hw, unsigned long rate, 40945810d48SGuillaume Ranquet unsigned long parent_rate) 41045810d48SGuillaume Ranquet { 41145810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 41245810d48SGuillaume Ranquet 41345810d48SGuillaume Ranquet dev_dbg(hdmi_phy->dev, "%s: %lu Hz, parent: %lu Hz\n", __func__, rate, 41445810d48SGuillaume Ranquet parent_rate); 41545810d48SGuillaume Ranquet 41645810d48SGuillaume Ranquet return mtk_hdmi_pll_calc(hdmi_phy, hw, rate, parent_rate); 41745810d48SGuillaume Ranquet } 41845810d48SGuillaume Ranquet 41945810d48SGuillaume Ranquet static long mtk_hdmi_pll_round_rate(struct clk_hw *hw, unsigned long rate, 42045810d48SGuillaume Ranquet unsigned long *parent_rate) 42145810d48SGuillaume Ranquet { 42245810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 42345810d48SGuillaume Ranquet 42445810d48SGuillaume Ranquet hdmi_phy->pll_rate = rate; 42545810d48SGuillaume Ranquet return rate; 42645810d48SGuillaume Ranquet } 42745810d48SGuillaume Ranquet 42845810d48SGuillaume Ranquet static unsigned long mtk_hdmi_pll_recalc_rate(struct clk_hw *hw, 42945810d48SGuillaume Ranquet unsigned long parent_rate) 43045810d48SGuillaume Ranquet { 43145810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = to_mtk_hdmi_phy(hw); 43245810d48SGuillaume Ranquet 43345810d48SGuillaume Ranquet return hdmi_phy->pll_rate; 43445810d48SGuillaume Ranquet } 43545810d48SGuillaume Ranquet 43645810d48SGuillaume Ranquet static const struct clk_ops mtk_hdmi_pll_ops = { 43745810d48SGuillaume Ranquet .prepare = mtk_hdmi_pll_prepare, 43845810d48SGuillaume Ranquet .unprepare = mtk_hdmi_pll_unprepare, 43945810d48SGuillaume Ranquet .set_rate = mtk_hdmi_pll_set_rate, 44045810d48SGuillaume Ranquet .round_rate = mtk_hdmi_pll_round_rate, 44145810d48SGuillaume Ranquet .recalc_rate = mtk_hdmi_pll_recalc_rate, 44245810d48SGuillaume Ranquet }; 44345810d48SGuillaume Ranquet 44445810d48SGuillaume Ranquet static void vtx_signal_en(struct mtk_hdmi_phy *hdmi_phy, bool on) 44545810d48SGuillaume Ranquet { 44645810d48SGuillaume Ranquet void __iomem *regs = hdmi_phy->regs; 44745810d48SGuillaume Ranquet 44845810d48SGuillaume Ranquet if (on) 44945810d48SGuillaume Ranquet mtk_phy_set_bits(regs + HDMI_1_CFG_0, RG_HDMITX21_DRV_EN); 45045810d48SGuillaume Ranquet else 45145810d48SGuillaume Ranquet mtk_phy_clear_bits(regs + HDMI_1_CFG_0, RG_HDMITX21_DRV_EN); 45245810d48SGuillaume Ranquet } 45345810d48SGuillaume Ranquet 45445810d48SGuillaume Ranquet static void mtk_hdmi_phy_enable_tmds(struct mtk_hdmi_phy *hdmi_phy) 45545810d48SGuillaume Ranquet { 45645810d48SGuillaume Ranquet vtx_signal_en(hdmi_phy, true); 45745810d48SGuillaume Ranquet usleep_range(100, 150); 45845810d48SGuillaume Ranquet } 45945810d48SGuillaume Ranquet 46045810d48SGuillaume Ranquet static void mtk_hdmi_phy_disable_tmds(struct mtk_hdmi_phy *hdmi_phy) 46145810d48SGuillaume Ranquet { 46245810d48SGuillaume Ranquet vtx_signal_en(hdmi_phy, false); 46345810d48SGuillaume Ranquet } 46445810d48SGuillaume Ranquet 46545810d48SGuillaume Ranquet static int mtk_hdmi_phy_configure(struct phy *phy, union phy_configure_opts *opts) 46645810d48SGuillaume Ranquet { 46745810d48SGuillaume Ranquet struct phy_configure_opts_dp *dp_opts = &opts->dp; 46845810d48SGuillaume Ranquet struct mtk_hdmi_phy *hdmi_phy = phy_get_drvdata(phy); 46945810d48SGuillaume Ranquet int ret; 47045810d48SGuillaume Ranquet 47145810d48SGuillaume Ranquet ret = clk_set_rate(hdmi_phy->pll, dp_opts->link_rate); 47245810d48SGuillaume Ranquet 47345810d48SGuillaume Ranquet if (ret) 47445810d48SGuillaume Ranquet return ret; 47545810d48SGuillaume Ranquet 47645810d48SGuillaume Ranquet mtk_phy_tmds_clk_ratio(hdmi_phy, hdmi_phy->tmds_over_340M); 47745810d48SGuillaume Ranquet 47845810d48SGuillaume Ranquet return ret; 47945810d48SGuillaume Ranquet } 48045810d48SGuillaume Ranquet 48145810d48SGuillaume Ranquet struct mtk_hdmi_phy_conf mtk_hdmi_phy_8195_conf = { 48245810d48SGuillaume Ranquet .flags = CLK_SET_RATE_PARENT | CLK_SET_RATE_GATE, 48345810d48SGuillaume Ranquet .hdmi_phy_clk_ops = &mtk_hdmi_pll_ops, 48445810d48SGuillaume Ranquet .hdmi_phy_enable_tmds = mtk_hdmi_phy_enable_tmds, 48545810d48SGuillaume Ranquet .hdmi_phy_disable_tmds = mtk_hdmi_phy_disable_tmds, 48645810d48SGuillaume Ranquet .hdmi_phy_configure = mtk_hdmi_phy_configure, 48745810d48SGuillaume Ranquet }; 48845810d48SGuillaume Ranquet 48945810d48SGuillaume Ranquet MODULE_AUTHOR("Can Zeng <can.zeng@mediatek.com>"); 49045810d48SGuillaume Ranquet MODULE_DESCRIPTION("MediaTek MT8195 HDMI PHY Driver"); 49145810d48SGuillaume Ranquet MODULE_LICENSE("GPL v2"); 492