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