1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // Copyright (c) 2021, ASPEED Technology Inc. |
3 | // Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com> |
4 | |
5 | #include <linux/firmware.h> |
6 | #include <linux/delay.h> |
7 | #include <drm/drm_print.h> |
8 | #include "ast_drv.h" |
9 | |
10 | bool ast_astdp_is_connected(struct ast_device *ast) |
11 | { |
12 | if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, ASTDP_MCU_FW_EXECUTING)) |
13 | return false; |
14 | if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, ASTDP_HPD)) |
15 | return false; |
16 | if (!ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDC, ASTDP_LINK_SUCCESS)) |
17 | return false; |
18 | return true; |
19 | } |
20 | |
21 | int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata) |
22 | { |
23 | struct ast_device *ast = to_ast_device(dev); |
24 | u8 i = 0, j = 0; |
25 | |
26 | /* |
27 | * CRD1[b5]: DP MCU FW is executing |
28 | * CRDC[b0]: DP link success |
29 | * CRDF[b0]: DP HPD |
30 | * CRE5[b0]: Host reading EDID process is done |
31 | */ |
32 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, ASTDP_MCU_FW_EXECUTING) && |
33 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDC, ASTDP_LINK_SUCCESS) && |
34 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, ASTDP_HPD) && |
35 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, |
36 | ASTDP_HOST_EDID_READ_DONE_MASK))) { |
37 | goto err_astdp_edid_not_ready; |
38 | } |
39 | |
40 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, preserve_mask: (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, |
41 | val: 0x00); |
42 | |
43 | for (i = 0; i < 32; i++) { |
44 | /* |
45 | * CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64 |
46 | */ |
47 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE4, |
48 | ASTDP_AND_CLEAR_MASK, val: (u8)i); |
49 | j = 0; |
50 | |
51 | /* |
52 | * CRD7[b0]: valid flag for EDID |
53 | * CRD6[b0]: mirror read pointer for EDID |
54 | */ |
55 | while ((ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD7, |
56 | ASTDP_EDID_VALID_FLAG_MASK) != 0x01) || |
57 | (ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD6, |
58 | ASTDP_EDID_READ_POINTER_MASK) != i)) { |
59 | /* |
60 | * Delay are getting longer with each retry. |
61 | * 1. The Delays are often 2 loops when users request "Display Settings" |
62 | * of right-click of mouse. |
63 | * 2. The Delays are often longer a lot when system resume from S3/S4. |
64 | */ |
65 | mdelay(j+1); |
66 | |
67 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, |
68 | ASTDP_MCU_FW_EXECUTING) && |
69 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDC, |
70 | ASTDP_LINK_SUCCESS) && |
71 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, ASTDP_HPD))) { |
72 | goto err_astdp_jump_out_loop_of_edid; |
73 | } |
74 | |
75 | j++; |
76 | if (j > 200) |
77 | goto err_astdp_jump_out_loop_of_edid; |
78 | } |
79 | |
80 | *(ediddata) = ast_get_index_reg_mask(ast, AST_IO_VGACRI, |
81 | index: 0xD8, ASTDP_EDID_READ_DATA_MASK); |
82 | *(ediddata + 1) = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD9, |
83 | ASTDP_EDID_READ_DATA_MASK); |
84 | *(ediddata + 2) = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDA, |
85 | ASTDP_EDID_READ_DATA_MASK); |
86 | *(ediddata + 3) = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDB, |
87 | ASTDP_EDID_READ_DATA_MASK); |
88 | |
89 | if (i == 31) { |
90 | /* |
91 | * For 128-bytes EDID_1.3, |
92 | * 1. Add the value of Bytes-126 to Bytes-127. |
93 | * The Bytes-127 is Checksum. Sum of all 128bytes should |
94 | * equal 0 (mod 256). |
95 | * 2. Modify Bytes-126 to be 0. |
96 | * The Bytes-126 indicates the Number of extensions to |
97 | * follow. 0 represents noextensions. |
98 | */ |
99 | *(ediddata + 3) = *(ediddata + 3) + *(ediddata + 2); |
100 | *(ediddata + 2) = 0; |
101 | } |
102 | |
103 | ediddata += 4; |
104 | } |
105 | |
106 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, preserve_mask: (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, |
107 | ASTDP_HOST_EDID_READ_DONE); |
108 | |
109 | return 0; |
110 | |
111 | err_astdp_jump_out_loop_of_edid: |
112 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, |
113 | preserve_mask: (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, |
114 | ASTDP_HOST_EDID_READ_DONE); |
115 | return (~(j+256) + 1); |
116 | |
117 | err_astdp_edid_not_ready: |
118 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, ASTDP_MCU_FW_EXECUTING))) |
119 | return (~0xD1 + 1); |
120 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDC, ASTDP_LINK_SUCCESS))) |
121 | return (~0xDC + 1); |
122 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, ASTDP_HPD))) |
123 | return (~0xDF + 1); |
124 | if (!(ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, ASTDP_HOST_EDID_READ_DONE_MASK))) |
125 | return (~0xE5 + 1); |
126 | |
127 | return 0; |
128 | } |
129 | |
130 | /* |
131 | * Launch Aspeed DP |
132 | */ |
133 | void ast_dp_launch(struct drm_device *dev) |
134 | { |
135 | u32 i = 0; |
136 | u8 bDPExecute = 1; |
137 | struct ast_device *ast = to_ast_device(dev); |
138 | |
139 | // Wait one second then timeout. |
140 | while (ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xD1, ASTDP_MCU_FW_EXECUTING) != |
141 | ASTDP_MCU_FW_EXECUTING) { |
142 | i++; |
143 | // wait 100 ms |
144 | msleep(msecs: 100); |
145 | |
146 | if (i >= 10) { |
147 | // DP would not be ready. |
148 | bDPExecute = 0; |
149 | break; |
150 | } |
151 | } |
152 | |
153 | if (!bDPExecute) |
154 | drm_err(dev, "Wait DPMCU executing timeout\n" ); |
155 | |
156 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE5, |
157 | preserve_mask: (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK, |
158 | ASTDP_HOST_EDID_READ_DONE); |
159 | } |
160 | |
161 | |
162 | |
163 | void ast_dp_power_on_off(struct drm_device *dev, bool on) |
164 | { |
165 | struct ast_device *ast = to_ast_device(dev); |
166 | // Read and Turn off DP PHY sleep |
167 | u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE3, AST_DP_VIDEO_ENABLE); |
168 | |
169 | // Turn on DP PHY sleep |
170 | if (!on) |
171 | bE3 |= AST_DP_PHY_SLEEP; |
172 | |
173 | // DP Power on/off |
174 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE3, preserve_mask: (u8) ~AST_DP_PHY_SLEEP, val: bE3); |
175 | } |
176 | |
177 | |
178 | |
179 | void ast_dp_set_on_off(struct drm_device *dev, bool on) |
180 | { |
181 | struct ast_device *ast = to_ast_device(dev); |
182 | u8 video_on_off = on; |
183 | u32 i = 0; |
184 | |
185 | // Video On/Off |
186 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE3, preserve_mask: (u8) ~AST_DP_VIDEO_ENABLE, val: on); |
187 | |
188 | // If DP plug in and link successful then check video on / off status |
189 | if (ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDC, ASTDP_LINK_SUCCESS) && |
190 | ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, ASTDP_HPD)) { |
191 | video_on_off <<= 4; |
192 | while (ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xDF, |
193 | ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) { |
194 | // wait 1 ms |
195 | mdelay(1); |
196 | if (++i > 200) |
197 | break; |
198 | } |
199 | } |
200 | } |
201 | |
202 | void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode) |
203 | { |
204 | struct ast_device *ast = to_ast_device(dev: crtc->dev); |
205 | |
206 | u32 ulRefreshRateIndex; |
207 | u8 ModeIdx; |
208 | |
209 | ulRefreshRateIndex = vbios_mode->enh_table->refresh_rate_index - 1; |
210 | |
211 | switch (crtc->mode.crtc_hdisplay) { |
212 | case 320: |
213 | ModeIdx = ASTDP_320x240_60; |
214 | break; |
215 | case 400: |
216 | ModeIdx = ASTDP_400x300_60; |
217 | break; |
218 | case 512: |
219 | ModeIdx = ASTDP_512x384_60; |
220 | break; |
221 | case 640: |
222 | ModeIdx = (ASTDP_640x480_60 + (u8) ulRefreshRateIndex); |
223 | break; |
224 | case 800: |
225 | ModeIdx = (ASTDP_800x600_56 + (u8) ulRefreshRateIndex); |
226 | break; |
227 | case 1024: |
228 | ModeIdx = (ASTDP_1024x768_60 + (u8) ulRefreshRateIndex); |
229 | break; |
230 | case 1152: |
231 | ModeIdx = ASTDP_1152x864_75; |
232 | break; |
233 | case 1280: |
234 | if (crtc->mode.crtc_vdisplay == 800) |
235 | ModeIdx = (ASTDP_1280x800_60_RB - (u8) ulRefreshRateIndex); |
236 | else // 1024 |
237 | ModeIdx = (ASTDP_1280x1024_60 + (u8) ulRefreshRateIndex); |
238 | break; |
239 | case 1360: |
240 | case 1366: |
241 | ModeIdx = ASTDP_1366x768_60; |
242 | break; |
243 | case 1440: |
244 | ModeIdx = (ASTDP_1440x900_60_RB - (u8) ulRefreshRateIndex); |
245 | break; |
246 | case 1600: |
247 | if (crtc->mode.crtc_vdisplay == 900) |
248 | ModeIdx = (ASTDP_1600x900_60_RB - (u8) ulRefreshRateIndex); |
249 | else //1200 |
250 | ModeIdx = ASTDP_1600x1200_60; |
251 | break; |
252 | case 1680: |
253 | ModeIdx = (ASTDP_1680x1050_60_RB - (u8) ulRefreshRateIndex); |
254 | break; |
255 | case 1920: |
256 | if (crtc->mode.crtc_vdisplay == 1080) |
257 | ModeIdx = ASTDP_1920x1080_60; |
258 | else //1200 |
259 | ModeIdx = ASTDP_1920x1200_60; |
260 | break; |
261 | default: |
262 | return; |
263 | } |
264 | |
265 | /* |
266 | * CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp) |
267 | * CRE1[7:0]: MISC1 (default: 0x00) |
268 | * CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50) |
269 | */ |
270 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE0, ASTDP_AND_CLEAR_MASK, |
271 | ASTDP_MISC0_24bpp); |
272 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE1, ASTDP_AND_CLEAR_MASK, ASTDP_MISC1); |
273 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xE2, ASTDP_AND_CLEAR_MASK, val: ModeIdx); |
274 | } |
275 | |