1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * HDMI PHY |
4 | * |
5 | * Copyright (C) 2013 Texas Instruments Incorporated - https://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(base_addr: 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(base_addr: phy->base, HDMI_TXPHY_DIGITAL_CTRL, val: 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 | if (version == 4) |
186 | phy->features = &omap44xx_phy_feats; |
187 | else |
188 | phy->features = &omap54xx_phy_feats; |
189 | |
190 | phy->base = devm_platform_ioremap_resource_byname(pdev, name: "phy" ); |
191 | if (IS_ERR(ptr: phy->base)) |
192 | return PTR_ERR(ptr: phy->base); |
193 | |
194 | return 0; |
195 | } |
196 | |