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