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