1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * HDMI PHY 4 * 5 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com/ 6 */ 7 8 #include <linux/kernel.h> 9 #include <linux/err.h> 10 #include <linux/io.h> 11 #include <linux/platform_device.h> 12 #include <linux/slab.h> 13 #include <linux/seq_file.h> 14 15 #include "omapdss.h" 16 #include "dss.h" 17 #include "hdmi.h" 18 19 void hdmi_phy_dump(struct hdmi_phy_data *phy, struct seq_file *s) 20 { 21 #define DUMPPHY(r) seq_printf(s, "%-35s %08x\n", #r,\ 22 hdmi_read_reg(phy->base, r)) 23 24 DUMPPHY(HDMI_TXPHY_TX_CTRL); 25 DUMPPHY(HDMI_TXPHY_DIGITAL_CTRL); 26 DUMPPHY(HDMI_TXPHY_POWER_CTRL); 27 DUMPPHY(HDMI_TXPHY_PAD_CFG_CTRL); 28 if (phy->features->bist_ctrl) 29 DUMPPHY(HDMI_TXPHY_BIST_CONTROL); 30 } 31 32 int hdmi_phy_parse_lanes(struct hdmi_phy_data *phy, const u32 *lanes) 33 { 34 int i; 35 36 for (i = 0; i < 8; i += 2) { 37 u8 lane, pol; 38 int dx, dy; 39 40 dx = lanes[i]; 41 dy = lanes[i + 1]; 42 43 if (dx < 0 || dx >= 8) 44 return -EINVAL; 45 46 if (dy < 0 || dy >= 8) 47 return -EINVAL; 48 49 if (dx & 1) { 50 if (dy != dx - 1) 51 return -EINVAL; 52 pol = 1; 53 } else { 54 if (dy != dx + 1) 55 return -EINVAL; 56 pol = 0; 57 } 58 59 lane = dx / 2; 60 61 phy->lane_function[lane] = i / 2; 62 phy->lane_polarity[lane] = pol; 63 } 64 65 return 0; 66 } 67 68 static void hdmi_phy_configure_lanes(struct hdmi_phy_data *phy) 69 { 70 static const u16 pad_cfg_list[] = { 71 0x0123, 72 0x0132, 73 0x0312, 74 0x0321, 75 0x0231, 76 0x0213, 77 0x1023, 78 0x1032, 79 0x3012, 80 0x3021, 81 0x2031, 82 0x2013, 83 0x1203, 84 0x1302, 85 0x3102, 86 0x3201, 87 0x2301, 88 0x2103, 89 0x1230, 90 0x1320, 91 0x3120, 92 0x3210, 93 0x2310, 94 0x2130, 95 }; 96 97 u16 lane_cfg = 0; 98 int i; 99 unsigned int lane_cfg_val; 100 u16 pol_val = 0; 101 102 for (i = 0; i < 4; ++i) 103 lane_cfg |= phy->lane_function[i] << ((3 - i) * 4); 104 105 pol_val |= phy->lane_polarity[0] << 0; 106 pol_val |= phy->lane_polarity[1] << 3; 107 pol_val |= phy->lane_polarity[2] << 2; 108 pol_val |= phy->lane_polarity[3] << 1; 109 110 for (i = 0; i < ARRAY_SIZE(pad_cfg_list); ++i) 111 if (pad_cfg_list[i] == lane_cfg) 112 break; 113 114 if (WARN_ON(i == ARRAY_SIZE(pad_cfg_list))) 115 i = 0; 116 117 lane_cfg_val = i; 118 119 REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, lane_cfg_val, 26, 22); 120 REG_FLD_MOD(phy->base, HDMI_TXPHY_PAD_CFG_CTRL, pol_val, 30, 27); 121 } 122 123 int hdmi_phy_configure(struct hdmi_phy_data *phy, unsigned long hfbitclk, 124 unsigned long lfbitclk) 125 { 126 u8 freqout; 127 128 /* 129 * Read address 0 in order to get the SCP reset done completed 130 * Dummy access performed to make sure reset is done 131 */ 132 hdmi_read_reg(phy->base, HDMI_TXPHY_TX_CTRL); 133 134 /* 135 * In OMAP5+, the HFBITCLK must be divided by 2 before issuing the 136 * HDMI_PHYPWRCMD_LDOON command. 137 */ 138 if (phy->features->bist_ctrl) 139 REG_FLD_MOD(phy->base, HDMI_TXPHY_BIST_CONTROL, 1, 11, 11); 140 141 /* 142 * If the hfbitclk != lfbitclk, it means the lfbitclk was configured 143 * to be used for TMDS. 144 */ 145 if (hfbitclk != lfbitclk) 146 freqout = 0; 147 else if (hfbitclk / 10 < phy->features->max_phy) 148 freqout = 1; 149 else 150 freqout = 2; 151 152 /* 153 * Write to phy address 0 to configure the clock 154 * use HFBITCLK write HDMI_TXPHY_TX_CONTROL_FREQOUT field 155 */ 156 REG_FLD_MOD(phy->base, HDMI_TXPHY_TX_CTRL, freqout, 31, 30); 157 158 /* Write to phy address 1 to start HDMI line (TXVALID and TMDSCLKEN) */ 159 hdmi_write_reg(phy->base, HDMI_TXPHY_DIGITAL_CTRL, 0xF0000000); 160 161 /* Setup max LDO voltage */ 162 if (phy->features->ldo_voltage) 163 REG_FLD_MOD(phy->base, HDMI_TXPHY_POWER_CTRL, 0xB, 3, 0); 164 165 hdmi_phy_configure_lanes(phy); 166 167 return 0; 168 } 169 170 static const struct hdmi_phy_features omap44xx_phy_feats = { 171 .bist_ctrl = false, 172 .ldo_voltage = true, 173 .max_phy = 185675000, 174 }; 175 176 static const struct hdmi_phy_features omap54xx_phy_feats = { 177 .bist_ctrl = true, 178 .ldo_voltage = false, 179 .max_phy = 186000000, 180 }; 181 182 int hdmi_phy_init(struct platform_device *pdev, struct hdmi_phy_data *phy, 183 unsigned int version) 184 { 185 struct resource *res; 186 187 if (version == 4) 188 phy->features = &omap44xx_phy_feats; 189 else 190 phy->features = &omap54xx_phy_feats; 191 192 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); 193 phy->base = devm_ioremap_resource(&pdev->dev, res); 194 if (IS_ERR(phy->base)) 195 return PTR_ERR(phy->base); 196 197 return 0; 198 } 199