]>
Commit | Line | Data |
---|---|---|
fcfe0bdc MC |
1 | /* |
2 | * Copyright © 2018 Intel Corporation | |
3 | * | |
4 | * Permission is hereby granted, free of charge, to any person obtaining a | |
5 | * copy of this software and associated documentation files (the "Software"), | |
6 | * to deal in the Software without restriction, including without limitation | |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
8 | * and/or sell copies of the Software, and to permit persons to whom the | |
9 | * Software is furnished to do so, subject to the following conditions: | |
10 | * | |
11 | * The above copyright notice and this permission notice (including the next | |
12 | * paragraph) shall be included in all copies or substantial portions of the | |
13 | * Software. | |
14 | * | |
15 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
16 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
17 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
18 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
19 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING | |
20 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
21 | * DEALINGS IN THE SOFTWARE. | |
22 | * | |
23 | * Authors: | |
24 | * Madhav Chauhan <madhav.chauhan@intel.com> | |
25 | * Jani Nikula <jani.nikula@intel.com> | |
26 | */ | |
27 | ||
28 | #include "intel_dsi.h" | |
29 | ||
3f4b9d9d MC |
30 | static void dsi_program_swing_and_deemphasis(struct intel_encoder *encoder) |
31 | { | |
32 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
33 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
34 | enum port port; | |
35 | u32 tmp; | |
36 | int lane; | |
37 | ||
38 | for_each_dsi_port(port, intel_dsi->ports) { | |
39 | ||
40 | /* | |
41 | * Program voltage swing and pre-emphasis level values as per | |
42 | * table in BSPEC under DDI buffer programing | |
43 | */ | |
44 | tmp = I915_READ(ICL_PORT_TX_DW5_LN0(port)); | |
45 | tmp &= ~(SCALING_MODE_SEL_MASK | RTERM_SELECT_MASK); | |
46 | tmp |= SCALING_MODE_SEL(0x2); | |
47 | tmp |= TAP2_DISABLE | TAP3_DISABLE; | |
48 | tmp |= RTERM_SELECT(0x6); | |
49 | I915_WRITE(ICL_PORT_TX_DW5_GRP(port), tmp); | |
50 | ||
51 | tmp = I915_READ(ICL_PORT_TX_DW5_AUX(port)); | |
52 | tmp &= ~(SCALING_MODE_SEL_MASK | RTERM_SELECT_MASK); | |
53 | tmp |= SCALING_MODE_SEL(0x2); | |
54 | tmp |= TAP2_DISABLE | TAP3_DISABLE; | |
55 | tmp |= RTERM_SELECT(0x6); | |
56 | I915_WRITE(ICL_PORT_TX_DW5_AUX(port), tmp); | |
57 | ||
58 | tmp = I915_READ(ICL_PORT_TX_DW2_LN0(port)); | |
59 | tmp &= ~(SWING_SEL_LOWER_MASK | SWING_SEL_UPPER_MASK | | |
60 | RCOMP_SCALAR_MASK); | |
61 | tmp |= SWING_SEL_UPPER(0x2); | |
62 | tmp |= SWING_SEL_LOWER(0x2); | |
63 | tmp |= RCOMP_SCALAR(0x98); | |
64 | I915_WRITE(ICL_PORT_TX_DW2_GRP(port), tmp); | |
65 | ||
66 | tmp = I915_READ(ICL_PORT_TX_DW2_AUX(port)); | |
67 | tmp &= ~(SWING_SEL_LOWER_MASK | SWING_SEL_UPPER_MASK | | |
68 | RCOMP_SCALAR_MASK); | |
69 | tmp |= SWING_SEL_UPPER(0x2); | |
70 | tmp |= SWING_SEL_LOWER(0x2); | |
71 | tmp |= RCOMP_SCALAR(0x98); | |
72 | I915_WRITE(ICL_PORT_TX_DW2_AUX(port), tmp); | |
73 | ||
74 | tmp = I915_READ(ICL_PORT_TX_DW4_AUX(port)); | |
75 | tmp &= ~(POST_CURSOR_1_MASK | POST_CURSOR_2_MASK | | |
76 | CURSOR_COEFF_MASK); | |
77 | tmp |= POST_CURSOR_1(0x0); | |
78 | tmp |= POST_CURSOR_2(0x0); | |
79 | tmp |= CURSOR_COEFF(0x3f); | |
80 | I915_WRITE(ICL_PORT_TX_DW4_AUX(port), tmp); | |
81 | ||
82 | for (lane = 0; lane <= 3; lane++) { | |
83 | /* Bspec: must not use GRP register for write */ | |
84 | tmp = I915_READ(ICL_PORT_TX_DW4_LN(port, lane)); | |
85 | tmp &= ~(POST_CURSOR_1_MASK | POST_CURSOR_2_MASK | | |
86 | CURSOR_COEFF_MASK); | |
87 | tmp |= POST_CURSOR_1(0x0); | |
88 | tmp |= POST_CURSOR_2(0x0); | |
89 | tmp |= CURSOR_COEFF(0x3f); | |
90 | I915_WRITE(ICL_PORT_TX_DW4_LN(port, lane), tmp); | |
91 | } | |
92 | } | |
93 | } | |
94 | ||
fcfe0bdc MC |
95 | static void gen11_dsi_program_esc_clk_div(struct intel_encoder *encoder) |
96 | { | |
97 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
98 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
99 | enum port port; | |
100 | u32 bpp = mipi_dsi_pixel_format_to_bpp(intel_dsi->pixel_format); | |
101 | u32 afe_clk_khz; /* 8X Clock */ | |
102 | u32 esc_clk_div_m; | |
103 | ||
104 | afe_clk_khz = DIV_ROUND_CLOSEST(intel_dsi->pclk * bpp, | |
105 | intel_dsi->lane_count); | |
106 | ||
107 | esc_clk_div_m = DIV_ROUND_UP(afe_clk_khz, DSI_MAX_ESC_CLK); | |
108 | ||
109 | for_each_dsi_port(port, intel_dsi->ports) { | |
110 | I915_WRITE(ICL_DSI_ESC_CLK_DIV(port), | |
111 | esc_clk_div_m & ICL_ESC_CLK_DIV_MASK); | |
112 | POSTING_READ(ICL_DSI_ESC_CLK_DIV(port)); | |
113 | } | |
114 | ||
115 | for_each_dsi_port(port, intel_dsi->ports) { | |
116 | I915_WRITE(ICL_DPHY_ESC_CLK_DIV(port), | |
117 | esc_clk_div_m & ICL_ESC_CLK_DIV_MASK); | |
118 | POSTING_READ(ICL_DPHY_ESC_CLK_DIV(port)); | |
119 | } | |
120 | } | |
121 | ||
b1cb21a5 MC |
122 | static void gen11_dsi_enable_io_power(struct intel_encoder *encoder) |
123 | { | |
124 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
125 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
126 | enum port port; | |
127 | u32 tmp; | |
128 | ||
129 | for_each_dsi_port(port, intel_dsi->ports) { | |
130 | tmp = I915_READ(ICL_DSI_IO_MODECTL(port)); | |
131 | tmp |= COMBO_PHY_MODE_DSI; | |
132 | I915_WRITE(ICL_DSI_IO_MODECTL(port), tmp); | |
133 | } | |
134 | ||
135 | for_each_dsi_port(port, intel_dsi->ports) { | |
136 | intel_display_power_get(dev_priv, port == PORT_A ? | |
137 | POWER_DOMAIN_PORT_DDI_A_IO : | |
138 | POWER_DOMAIN_PORT_DDI_B_IO); | |
139 | } | |
140 | } | |
141 | ||
45f09f7a MC |
142 | static void gen11_dsi_power_up_lanes(struct intel_encoder *encoder) |
143 | { | |
144 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
145 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
146 | enum port port; | |
147 | u32 tmp; | |
148 | u32 lane_mask; | |
149 | ||
150 | switch (intel_dsi->lane_count) { | |
151 | case 1: | |
152 | lane_mask = PWR_DOWN_LN_3_1_0; | |
153 | break; | |
154 | case 2: | |
155 | lane_mask = PWR_DOWN_LN_3_1; | |
156 | break; | |
157 | case 3: | |
158 | lane_mask = PWR_DOWN_LN_3; | |
159 | break; | |
160 | case 4: | |
161 | default: | |
162 | lane_mask = PWR_UP_ALL_LANES; | |
163 | break; | |
164 | } | |
165 | ||
166 | for_each_dsi_port(port, intel_dsi->ports) { | |
167 | tmp = I915_READ(ICL_PORT_CL_DW10(port)); | |
168 | tmp &= ~PWR_DOWN_LN_MASK; | |
169 | I915_WRITE(ICL_PORT_CL_DW10(port), tmp | lane_mask); | |
170 | } | |
171 | } | |
172 | ||
fc41001d MC |
173 | static void gen11_dsi_config_phy_lanes_sequence(struct intel_encoder *encoder) |
174 | { | |
175 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
176 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
177 | enum port port; | |
178 | u32 tmp; | |
179 | int lane; | |
180 | ||
181 | /* Step 4b(i) set loadgen select for transmit and aux lanes */ | |
182 | for_each_dsi_port(port, intel_dsi->ports) { | |
183 | tmp = I915_READ(ICL_PORT_TX_DW4_AUX(port)); | |
184 | tmp &= ~LOADGEN_SELECT; | |
185 | I915_WRITE(ICL_PORT_TX_DW4_AUX(port), tmp); | |
186 | for (lane = 0; lane <= 3; lane++) { | |
187 | tmp = I915_READ(ICL_PORT_TX_DW4_LN(port, lane)); | |
188 | tmp &= ~LOADGEN_SELECT; | |
189 | if (lane != 2) | |
190 | tmp |= LOADGEN_SELECT; | |
191 | I915_WRITE(ICL_PORT_TX_DW4_LN(port, lane), tmp); | |
192 | } | |
193 | } | |
194 | ||
195 | /* Step 4b(ii) set latency optimization for transmit and aux lanes */ | |
196 | for_each_dsi_port(port, intel_dsi->ports) { | |
197 | tmp = I915_READ(ICL_PORT_TX_DW2_AUX(port)); | |
198 | tmp &= ~FRC_LATENCY_OPTIM_MASK; | |
199 | tmp |= FRC_LATENCY_OPTIM_VAL(0x5); | |
200 | I915_WRITE(ICL_PORT_TX_DW2_AUX(port), tmp); | |
201 | tmp = I915_READ(ICL_PORT_TX_DW2_LN0(port)); | |
202 | tmp &= ~FRC_LATENCY_OPTIM_MASK; | |
203 | tmp |= FRC_LATENCY_OPTIM_VAL(0x5); | |
204 | I915_WRITE(ICL_PORT_TX_DW2_GRP(port), tmp); | |
205 | } | |
206 | ||
207 | } | |
208 | ||
3f4b9d9d MC |
209 | static void gen11_dsi_voltage_swing_program_seq(struct intel_encoder *encoder) |
210 | { | |
211 | struct drm_i915_private *dev_priv = to_i915(encoder->base.dev); | |
212 | struct intel_dsi *intel_dsi = enc_to_intel_dsi(&encoder->base); | |
213 | u32 tmp; | |
214 | enum port port; | |
215 | ||
216 | /* clear common keeper enable bit */ | |
217 | for_each_dsi_port(port, intel_dsi->ports) { | |
218 | tmp = I915_READ(ICL_PORT_PCS_DW1_LN0(port)); | |
219 | tmp &= ~COMMON_KEEPER_EN; | |
220 | I915_WRITE(ICL_PORT_PCS_DW1_GRP(port), tmp); | |
221 | tmp = I915_READ(ICL_PORT_PCS_DW1_AUX(port)); | |
222 | tmp &= ~COMMON_KEEPER_EN; | |
223 | I915_WRITE(ICL_PORT_PCS_DW1_AUX(port), tmp); | |
224 | } | |
225 | ||
226 | /* | |
227 | * Set SUS Clock Config bitfield to 11b | |
228 | * Note: loadgen select program is done | |
229 | * as part of lane phy sequence configuration | |
230 | */ | |
231 | for_each_dsi_port(port, intel_dsi->ports) { | |
232 | tmp = I915_READ(ICL_PORT_CL_DW5(port)); | |
233 | tmp |= SUS_CLOCK_CONFIG; | |
234 | I915_WRITE(ICL_PORT_CL_DW5(port), tmp); | |
235 | } | |
236 | ||
237 | /* Clear training enable to change swing values */ | |
238 | for_each_dsi_port(port, intel_dsi->ports) { | |
239 | tmp = I915_READ(ICL_PORT_TX_DW5_LN0(port)); | |
240 | tmp &= ~TX_TRAINING_EN; | |
241 | I915_WRITE(ICL_PORT_TX_DW5_GRP(port), tmp); | |
242 | tmp = I915_READ(ICL_PORT_TX_DW5_AUX(port)); | |
243 | tmp &= ~TX_TRAINING_EN; | |
244 | I915_WRITE(ICL_PORT_TX_DW5_AUX(port), tmp); | |
245 | } | |
246 | ||
247 | /* Program swing and de-emphasis */ | |
248 | dsi_program_swing_and_deemphasis(encoder); | |
249 | ||
250 | /* Set training enable to trigger update */ | |
251 | for_each_dsi_port(port, intel_dsi->ports) { | |
252 | tmp = I915_READ(ICL_PORT_TX_DW5_LN0(port)); | |
253 | tmp |= TX_TRAINING_EN; | |
254 | I915_WRITE(ICL_PORT_TX_DW5_GRP(port), tmp); | |
255 | tmp = I915_READ(ICL_PORT_TX_DW5_AUX(port)); | |
256 | tmp |= TX_TRAINING_EN; | |
257 | I915_WRITE(ICL_PORT_TX_DW5_AUX(port), tmp); | |
258 | } | |
259 | } | |
260 | ||
45f09f7a MC |
261 | static void gen11_dsi_enable_port_and_phy(struct intel_encoder *encoder) |
262 | { | |
263 | /* step 4a: power up all lanes of the DDI used by DSI */ | |
264 | gen11_dsi_power_up_lanes(encoder); | |
fc41001d MC |
265 | |
266 | /* step 4b: configure lane sequencing of the Combo-PHY transmitters */ | |
267 | gen11_dsi_config_phy_lanes_sequence(encoder); | |
3f4b9d9d MC |
268 | |
269 | /* step 4c: configure voltage swing and skew */ | |
270 | gen11_dsi_voltage_swing_program_seq(encoder); | |
45f09f7a MC |
271 | } |
272 | ||
fcfe0bdc MC |
273 | static void __attribute__((unused)) |
274 | gen11_dsi_pre_enable(struct intel_encoder *encoder, | |
275 | const struct intel_crtc_state *pipe_config, | |
276 | const struct drm_connector_state *conn_state) | |
277 | { | |
b1cb21a5 MC |
278 | /* step2: enable IO power */ |
279 | gen11_dsi_enable_io_power(encoder); | |
280 | ||
fcfe0bdc MC |
281 | /* step3: enable DSI PLL */ |
282 | gen11_dsi_program_esc_clk_div(encoder); | |
45f09f7a MC |
283 | |
284 | /* step4: enable DSI port and DPHY */ | |
285 | gen11_dsi_enable_port_and_phy(encoder); | |
fcfe0bdc | 286 | } |