1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | #include <linux/delay.h> |
4 | #include <linux/firmware.h> |
5 | #include <linux/module.h> |
6 | |
7 | #include "ast_drv.h" |
8 | |
9 | MODULE_FIRMWARE("ast_dp501_fw.bin" ); |
10 | |
11 | static void ast_release_firmware(void *data) |
12 | { |
13 | struct ast_device *ast = data; |
14 | |
15 | release_firmware(fw: ast->dp501_fw); |
16 | ast->dp501_fw = NULL; |
17 | } |
18 | |
19 | static int ast_load_dp501_microcode(struct drm_device *dev) |
20 | { |
21 | struct ast_device *ast = to_ast_device(dev); |
22 | int ret; |
23 | |
24 | ret = request_firmware(fw: &ast->dp501_fw, name: "ast_dp501_fw.bin" , device: dev->dev); |
25 | if (ret) |
26 | return ret; |
27 | |
28 | return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast); |
29 | } |
30 | |
31 | static void send_ack(struct ast_device *ast) |
32 | { |
33 | u8 sendack; |
34 | sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: 0xff); |
35 | sendack |= 0x80; |
36 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: 0x00, val: sendack); |
37 | } |
38 | |
39 | static void send_nack(struct ast_device *ast) |
40 | { |
41 | u8 sendack; |
42 | sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: 0xff); |
43 | sendack &= ~0x80; |
44 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: 0x00, val: sendack); |
45 | } |
46 | |
47 | static bool wait_ack(struct ast_device *ast) |
48 | { |
49 | u8 waitack; |
50 | u32 retry = 0; |
51 | do { |
52 | waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd2, preserve_mask: 0xff); |
53 | waitack &= 0x80; |
54 | udelay(100); |
55 | } while ((!waitack) && (retry++ < 1000)); |
56 | |
57 | if (retry < 1000) |
58 | return true; |
59 | else |
60 | return false; |
61 | } |
62 | |
63 | static bool wait_nack(struct ast_device *ast) |
64 | { |
65 | u8 waitack; |
66 | u32 retry = 0; |
67 | do { |
68 | waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd2, preserve_mask: 0xff); |
69 | waitack &= 0x80; |
70 | udelay(100); |
71 | } while ((waitack) && (retry++ < 1000)); |
72 | |
73 | if (retry < 1000) |
74 | return true; |
75 | else |
76 | return false; |
77 | } |
78 | |
79 | static void set_cmd_trigger(struct ast_device *ast) |
80 | { |
81 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: ~0x40, val: 0x40); |
82 | } |
83 | |
84 | static void clear_cmd_trigger(struct ast_device *ast) |
85 | { |
86 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9b, preserve_mask: ~0x40, val: 0x00); |
87 | } |
88 | |
89 | #if 0 |
90 | static bool wait_fw_ready(struct ast_device *ast) |
91 | { |
92 | u8 waitready; |
93 | u32 retry = 0; |
94 | do { |
95 | waitready = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); |
96 | waitready &= 0x40; |
97 | udelay(100); |
98 | } while ((!waitready) && (retry++ < 1000)); |
99 | |
100 | if (retry < 1000) |
101 | return true; |
102 | else |
103 | return false; |
104 | } |
105 | #endif |
106 | |
107 | static bool ast_write_cmd(struct drm_device *dev, u8 data) |
108 | { |
109 | struct ast_device *ast = to_ast_device(dev); |
110 | int retry = 0; |
111 | if (wait_nack(ast)) { |
112 | send_nack(ast); |
113 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9a, preserve_mask: 0x00, val: data); |
114 | send_ack(ast); |
115 | set_cmd_trigger(ast); |
116 | do { |
117 | if (wait_ack(ast)) { |
118 | clear_cmd_trigger(ast); |
119 | send_nack(ast); |
120 | return true; |
121 | } |
122 | } while (retry++ < 100); |
123 | } |
124 | clear_cmd_trigger(ast); |
125 | send_nack(ast); |
126 | return false; |
127 | } |
128 | |
129 | static bool ast_write_data(struct drm_device *dev, u8 data) |
130 | { |
131 | struct ast_device *ast = to_ast_device(dev); |
132 | |
133 | if (wait_nack(ast)) { |
134 | send_nack(ast); |
135 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0x9a, preserve_mask: 0x00, val: data); |
136 | send_ack(ast); |
137 | if (wait_ack(ast)) { |
138 | send_nack(ast); |
139 | return true; |
140 | } |
141 | } |
142 | send_nack(ast); |
143 | return false; |
144 | } |
145 | |
146 | #if 0 |
147 | static bool ast_read_data(struct drm_device *dev, u8 *data) |
148 | { |
149 | struct ast_device *ast = to_ast_device(dev); |
150 | u8 tmp; |
151 | |
152 | *data = 0; |
153 | |
154 | if (wait_ack(ast) == false) |
155 | return false; |
156 | tmp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd3, 0xff); |
157 | *data = tmp; |
158 | if (wait_nack(ast) == false) { |
159 | send_nack(ast); |
160 | return false; |
161 | } |
162 | send_nack(ast); |
163 | return true; |
164 | } |
165 | |
166 | static void clear_cmd(struct ast_device *ast) |
167 | { |
168 | send_nack(ast); |
169 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, 0x00); |
170 | } |
171 | #endif |
172 | |
173 | void ast_set_dp501_video_output(struct drm_device *dev, u8 mode) |
174 | { |
175 | ast_write_cmd(dev, data: 0x40); |
176 | ast_write_data(dev, data: mode); |
177 | |
178 | msleep(msecs: 10); |
179 | } |
180 | |
181 | static u32 get_fw_base(struct ast_device *ast) |
182 | { |
183 | return ast_mindwm(ast, r: 0x1e6e2104) & 0x7fffffff; |
184 | } |
185 | |
186 | bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) |
187 | { |
188 | struct ast_device *ast = to_ast_device(dev); |
189 | u32 i, data; |
190 | u32 boot_address; |
191 | |
192 | if (ast->config_mode != ast_use_p2a) |
193 | return false; |
194 | |
195 | data = ast_mindwm(ast, r: 0x1e6e2100) & 0x01; |
196 | if (data) { |
197 | boot_address = get_fw_base(ast); |
198 | for (i = 0; i < size; i += 4) |
199 | *(u32 *)(addr + i) = ast_mindwm(ast, r: boot_address + i); |
200 | return true; |
201 | } |
202 | return false; |
203 | } |
204 | |
205 | static bool ast_launch_m68k(struct drm_device *dev) |
206 | { |
207 | struct ast_device *ast = to_ast_device(dev); |
208 | u32 i, data, len = 0; |
209 | u32 boot_address; |
210 | u8 *fw_addr = NULL; |
211 | u8 jreg; |
212 | |
213 | if (ast->config_mode != ast_use_p2a) |
214 | return false; |
215 | |
216 | data = ast_mindwm(ast, r: 0x1e6e2100) & 0x01; |
217 | if (!data) { |
218 | |
219 | if (ast->dp501_fw_addr) { |
220 | fw_addr = ast->dp501_fw_addr; |
221 | len = 32*1024; |
222 | } else { |
223 | if (!ast->dp501_fw && |
224 | ast_load_dp501_microcode(dev) < 0) |
225 | return false; |
226 | |
227 | fw_addr = (u8 *)ast->dp501_fw->data; |
228 | len = ast->dp501_fw->size; |
229 | } |
230 | /* Get BootAddress */ |
231 | ast_moutdwm(ast, r: 0x1e6e2000, v: 0x1688a8a8); |
232 | data = ast_mindwm(ast, r: 0x1e6e0004); |
233 | switch (data & 0x03) { |
234 | case 0: |
235 | boot_address = 0x44000000; |
236 | break; |
237 | default: |
238 | case 1: |
239 | boot_address = 0x48000000; |
240 | break; |
241 | case 2: |
242 | boot_address = 0x50000000; |
243 | break; |
244 | case 3: |
245 | boot_address = 0x60000000; |
246 | break; |
247 | } |
248 | boot_address -= 0x200000; /* -2MB */ |
249 | |
250 | /* copy image to buffer */ |
251 | for (i = 0; i < len; i += 4) { |
252 | data = *(u32 *)(fw_addr + i); |
253 | ast_moutdwm(ast, r: boot_address + i, v: data); |
254 | } |
255 | |
256 | /* Init SCU */ |
257 | ast_moutdwm(ast, r: 0x1e6e2000, v: 0x1688a8a8); |
258 | |
259 | /* Launch FW */ |
260 | ast_moutdwm(ast, r: 0x1e6e2104, v: 0x80000000 + boot_address); |
261 | ast_moutdwm(ast, r: 0x1e6e2100, v: 1); |
262 | |
263 | /* Update Scratch */ |
264 | data = ast_mindwm(ast, r: 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */ |
265 | data |= 0x800; |
266 | ast_moutdwm(ast, r: 0x1e6e2040, v: data); |
267 | |
268 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0x99, preserve_mask: 0xfc); /* D[1:0]: Reserved Video Buffer */ |
269 | jreg |= 0x02; |
270 | ast_set_index_reg(ast, AST_IO_VGACRI, index: 0x99, val: jreg); |
271 | } |
272 | return true; |
273 | } |
274 | |
275 | bool ast_dp501_is_connected(struct ast_device *ast) |
276 | { |
277 | u32 boot_address, offset, data; |
278 | |
279 | if (ast->config_mode == ast_use_p2a) { |
280 | boot_address = get_fw_base(ast); |
281 | |
282 | /* validate FW version */ |
283 | offset = AST_DP501_GBL_VERSION; |
284 | data = ast_mindwm(ast, r: boot_address + offset); |
285 | if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) |
286 | return false; |
287 | |
288 | /* validate PnP Monitor */ |
289 | offset = AST_DP501_PNPMONITOR; |
290 | data = ast_mindwm(ast, r: boot_address + offset); |
291 | if (!(data & AST_DP501_PNP_CONNECTED)) |
292 | return false; |
293 | } else { |
294 | if (!ast->dp501_fw_buf) |
295 | return false; |
296 | |
297 | /* dummy read */ |
298 | offset = 0x0000; |
299 | data = readl(addr: ast->dp501_fw_buf + offset); |
300 | |
301 | /* validate FW version */ |
302 | offset = AST_DP501_GBL_VERSION; |
303 | data = readl(addr: ast->dp501_fw_buf + offset); |
304 | if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) |
305 | return false; |
306 | |
307 | /* validate PnP Monitor */ |
308 | offset = AST_DP501_PNPMONITOR; |
309 | data = readl(addr: ast->dp501_fw_buf + offset); |
310 | if (!(data & AST_DP501_PNP_CONNECTED)) |
311 | return false; |
312 | } |
313 | return true; |
314 | } |
315 | |
316 | bool ast_dp501_read_edid(struct drm_device *dev, u8 *ediddata) |
317 | { |
318 | struct ast_device *ast = to_ast_device(dev); |
319 | u32 i, boot_address, offset, data; |
320 | u32 *pEDIDidx; |
321 | |
322 | if (!ast_dp501_is_connected(ast)) |
323 | return false; |
324 | |
325 | if (ast->config_mode == ast_use_p2a) { |
326 | boot_address = get_fw_base(ast); |
327 | |
328 | /* Read EDID */ |
329 | offset = AST_DP501_EDID_DATA; |
330 | for (i = 0; i < 128; i += 4) { |
331 | data = ast_mindwm(ast, r: boot_address + offset + i); |
332 | pEDIDidx = (u32 *)(ediddata + i); |
333 | *pEDIDidx = data; |
334 | } |
335 | } else { |
336 | /* Read EDID */ |
337 | offset = AST_DP501_EDID_DATA; |
338 | for (i = 0; i < 128; i += 4) { |
339 | data = readl(addr: ast->dp501_fw_buf + offset + i); |
340 | pEDIDidx = (u32 *)(ediddata + i); |
341 | *pEDIDidx = data; |
342 | } |
343 | } |
344 | |
345 | return true; |
346 | } |
347 | |
348 | static bool ast_init_dvo(struct drm_device *dev) |
349 | { |
350 | struct ast_device *ast = to_ast_device(dev); |
351 | u8 jreg; |
352 | u32 data; |
353 | ast_write32(ast, reg: 0xf004, val: 0x1e6e0000); |
354 | ast_write32(ast, reg: 0xf000, val: 0x1); |
355 | ast_write32(ast, reg: 0x12000, val: 0x1688a8a8); |
356 | |
357 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd0, preserve_mask: 0xff); |
358 | if (!(jreg & 0x80)) { |
359 | /* Init SCU DVO Settings */ |
360 | data = ast_read32(ast, reg: 0x12008); |
361 | /* delay phase */ |
362 | data &= 0xfffff8ff; |
363 | data |= 0x00000500; |
364 | ast_write32(ast, reg: 0x12008, val: data); |
365 | |
366 | if (IS_AST_GEN4(ast)) { |
367 | data = ast_read32(ast, reg: 0x12084); |
368 | /* multi-pins for DVO single-edge */ |
369 | data |= 0xfffe0000; |
370 | ast_write32(ast, reg: 0x12084, val: data); |
371 | |
372 | data = ast_read32(ast, reg: 0x12088); |
373 | /* multi-pins for DVO single-edge */ |
374 | data |= 0x000fffff; |
375 | ast_write32(ast, reg: 0x12088, val: data); |
376 | |
377 | data = ast_read32(ast, reg: 0x12090); |
378 | /* multi-pins for DVO single-edge */ |
379 | data &= 0xffffffcf; |
380 | data |= 0x00000020; |
381 | ast_write32(ast, reg: 0x12090, val: data); |
382 | } else { /* AST GEN5+ */ |
383 | data = ast_read32(ast, reg: 0x12088); |
384 | /* multi-pins for DVO single-edge */ |
385 | data |= 0x30000000; |
386 | ast_write32(ast, reg: 0x12088, val: data); |
387 | |
388 | data = ast_read32(ast, reg: 0x1208c); |
389 | /* multi-pins for DVO single-edge */ |
390 | data |= 0x000000cf; |
391 | ast_write32(ast, reg: 0x1208c, val: data); |
392 | |
393 | data = ast_read32(ast, reg: 0x120a4); |
394 | /* multi-pins for DVO single-edge */ |
395 | data |= 0xffff0000; |
396 | ast_write32(ast, reg: 0x120a4, val: data); |
397 | |
398 | data = ast_read32(ast, reg: 0x120a8); |
399 | /* multi-pins for DVO single-edge */ |
400 | data |= 0x0000000f; |
401 | ast_write32(ast, reg: 0x120a8, val: data); |
402 | |
403 | data = ast_read32(ast, reg: 0x12094); |
404 | /* multi-pins for DVO single-edge */ |
405 | data |= 0x00000002; |
406 | ast_write32(ast, reg: 0x12094, val: data); |
407 | } |
408 | } |
409 | |
410 | /* Force to DVO */ |
411 | data = ast_read32(ast, reg: 0x1202c); |
412 | data &= 0xfffbffff; |
413 | ast_write32(ast, reg: 0x1202c, val: data); |
414 | |
415 | /* Init VGA DVO Settings */ |
416 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xa3, preserve_mask: 0xcf, val: 0x80); |
417 | return true; |
418 | } |
419 | |
420 | |
421 | static void ast_init_analog(struct drm_device *dev) |
422 | { |
423 | struct ast_device *ast = to_ast_device(dev); |
424 | u32 data; |
425 | |
426 | /* |
427 | * Set DAC source to VGA mode in SCU2C via the P2A |
428 | * bridge. First configure the P2U to target the SCU |
429 | * in case it isn't at this stage. |
430 | */ |
431 | ast_write32(ast, reg: 0xf004, val: 0x1e6e0000); |
432 | ast_write32(ast, reg: 0xf000, val: 0x1); |
433 | |
434 | /* Then unlock the SCU with the magic password */ |
435 | ast_write32(ast, reg: 0x12000, val: 0x1688a8a8); |
436 | ast_write32(ast, reg: 0x12000, val: 0x1688a8a8); |
437 | ast_write32(ast, reg: 0x12000, val: 0x1688a8a8); |
438 | |
439 | /* Finally, clear bits [17:16] of SCU2c */ |
440 | data = ast_read32(ast, reg: 0x1202c); |
441 | data &= 0xfffcffff; |
442 | ast_write32(ast, reg: 0, val: data); |
443 | |
444 | /* Disable DVO */ |
445 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, index: 0xa3, preserve_mask: 0xcf, val: 0x00); |
446 | } |
447 | |
448 | void ast_init_3rdtx(struct drm_device *dev) |
449 | { |
450 | struct ast_device *ast = to_ast_device(dev); |
451 | u8 jreg; |
452 | |
453 | if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { |
454 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, index: 0xd1, preserve_mask: 0xff); |
455 | switch (jreg & 0x0e) { |
456 | case 0x04: |
457 | ast_init_dvo(dev); |
458 | break; |
459 | case 0x08: |
460 | ast_launch_m68k(dev); |
461 | break; |
462 | case 0x0c: |
463 | ast_init_dvo(dev); |
464 | break; |
465 | default: |
466 | if (ast->tx_chip_types & BIT(AST_TX_SIL164)) |
467 | ast_init_dvo(dev); |
468 | else |
469 | ast_init_analog(dev); |
470 | } |
471 | } |
472 | } |
473 | |