Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
83c6620b | 2 | |
fbbbd160 | 3 | #include <linux/delay.h> |
83c6620b | 4 | #include <linux/firmware.h> |
fbbbd160 SR |
5 | #include <linux/module.h> |
6 | ||
f6d9f39f TZ |
7 | #include <drm/drm_atomic_state_helper.h> |
8 | #include <drm/drm_edid.h> | |
9 | #include <drm/drm_modeset_helper_vtables.h> | |
10 | #include <drm/drm_probe_helper.h> | |
11 | ||
83c6620b | 12 | #include "ast_drv.h" |
fbbbd160 | 13 | |
83c6620b DA |
14 | MODULE_FIRMWARE("ast_dp501_fw.bin"); |
15 | ||
2c0b6566 TZ |
16 | static void ast_release_firmware(void *data) |
17 | { | |
37b42cf9 | 18 | struct ast_device *ast = data; |
2c0b6566 TZ |
19 | |
20 | release_firmware(ast->dp501_fw); | |
21 | ast->dp501_fw = NULL; | |
22 | } | |
23 | ||
12f8030e | 24 | static int ast_load_dp501_microcode(struct drm_device *dev) |
83c6620b | 25 | { |
5abaa683 | 26 | struct ast_device *ast = to_ast_device(dev); |
2c0b6566 TZ |
27 | int ret; |
28 | ||
29 | ret = request_firmware(&ast->dp501_fw, "ast_dp501_fw.bin", dev->dev); | |
30 | if (ret) | |
31 | return ret; | |
83c6620b | 32 | |
2c0b6566 | 33 | return devm_add_action_or_reset(dev->dev, ast_release_firmware, ast); |
83c6620b DA |
34 | } |
35 | ||
37b42cf9 | 36 | static void send_ack(struct ast_device *ast) |
83c6620b DA |
37 | { |
38 | u8 sendack; | |
c79479fa | 39 | sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff); |
83c6620b | 40 | sendack |= 0x80; |
c79479fa | 41 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack); |
83c6620b DA |
42 | } |
43 | ||
37b42cf9 | 44 | static void send_nack(struct ast_device *ast) |
83c6620b DA |
45 | { |
46 | u8 sendack; | |
c79479fa | 47 | sendack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0xff); |
83c6620b | 48 | sendack &= ~0x80; |
c79479fa | 49 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, 0x00, sendack); |
83c6620b DA |
50 | } |
51 | ||
37b42cf9 | 52 | static bool wait_ack(struct ast_device *ast) |
83c6620b DA |
53 | { |
54 | u8 waitack; | |
55 | u32 retry = 0; | |
56 | do { | |
c79479fa | 57 | waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); |
83c6620b DA |
58 | waitack &= 0x80; |
59 | udelay(100); | |
60 | } while ((!waitack) && (retry++ < 1000)); | |
61 | ||
62 | if (retry < 1000) | |
63 | return true; | |
64 | else | |
65 | return false; | |
66 | } | |
67 | ||
37b42cf9 | 68 | static bool wait_nack(struct ast_device *ast) |
83c6620b DA |
69 | { |
70 | u8 waitack; | |
71 | u32 retry = 0; | |
72 | do { | |
c79479fa | 73 | waitack = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); |
83c6620b DA |
74 | waitack &= 0x80; |
75 | udelay(100); | |
76 | } while ((waitack) && (retry++ < 1000)); | |
77 | ||
78 | if (retry < 1000) | |
79 | return true; | |
80 | else | |
81 | return false; | |
82 | } | |
83 | ||
37b42cf9 | 84 | static void set_cmd_trigger(struct ast_device *ast) |
83c6620b | 85 | { |
c79479fa | 86 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x40); |
83c6620b DA |
87 | } |
88 | ||
37b42cf9 | 89 | static void clear_cmd_trigger(struct ast_device *ast) |
83c6620b | 90 | { |
c79479fa | 91 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9b, ~0x40, 0x00); |
83c6620b DA |
92 | } |
93 | ||
94 | #if 0 | |
37b42cf9 | 95 | static bool wait_fw_ready(struct ast_device *ast) |
83c6620b DA |
96 | { |
97 | u8 waitready; | |
98 | u32 retry = 0; | |
99 | do { | |
c79479fa | 100 | waitready = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd2, 0xff); |
83c6620b DA |
101 | waitready &= 0x40; |
102 | udelay(100); | |
103 | } while ((!waitready) && (retry++ < 1000)); | |
104 | ||
105 | if (retry < 1000) | |
106 | return true; | |
107 | else | |
108 | return false; | |
109 | } | |
110 | #endif | |
111 | ||
112 | static bool ast_write_cmd(struct drm_device *dev, u8 data) | |
113 | { | |
5abaa683 | 114 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
115 | int retry = 0; |
116 | if (wait_nack(ast)) { | |
117 | send_nack(ast); | |
c79479fa | 118 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data); |
83c6620b DA |
119 | send_ack(ast); |
120 | set_cmd_trigger(ast); | |
121 | do { | |
122 | if (wait_ack(ast)) { | |
123 | clear_cmd_trigger(ast); | |
124 | send_nack(ast); | |
125 | return true; | |
126 | } | |
127 | } while (retry++ < 100); | |
128 | } | |
129 | clear_cmd_trigger(ast); | |
130 | send_nack(ast); | |
131 | return false; | |
132 | } | |
133 | ||
134 | static bool ast_write_data(struct drm_device *dev, u8 data) | |
135 | { | |
5abaa683 | 136 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
137 | |
138 | if (wait_nack(ast)) { | |
139 | send_nack(ast); | |
c79479fa | 140 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, data); |
83c6620b DA |
141 | send_ack(ast); |
142 | if (wait_ack(ast)) { | |
143 | send_nack(ast); | |
144 | return true; | |
145 | } | |
146 | } | |
147 | send_nack(ast); | |
148 | return false; | |
149 | } | |
150 | ||
151 | #if 0 | |
152 | static bool ast_read_data(struct drm_device *dev, u8 *data) | |
153 | { | |
5abaa683 | 154 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
155 | u8 tmp; |
156 | ||
157 | *data = 0; | |
158 | ||
159 | if (wait_ack(ast) == false) | |
160 | return false; | |
c79479fa | 161 | tmp = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd3, 0xff); |
83c6620b DA |
162 | *data = tmp; |
163 | if (wait_nack(ast) == false) { | |
164 | send_nack(ast); | |
165 | return false; | |
166 | } | |
167 | send_nack(ast); | |
168 | return true; | |
169 | } | |
170 | ||
37b42cf9 | 171 | static void clear_cmd(struct ast_device *ast) |
83c6620b DA |
172 | { |
173 | send_nack(ast); | |
c79479fa | 174 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0x9a, 0x00, 0x00); |
83c6620b DA |
175 | } |
176 | #endif | |
177 | ||
f6d9f39f | 178 | static void ast_set_dp501_video_output(struct drm_device *dev, u8 mode) |
83c6620b DA |
179 | { |
180 | ast_write_cmd(dev, 0x40); | |
181 | ast_write_data(dev, mode); | |
182 | ||
183 | msleep(10); | |
184 | } | |
185 | ||
37b42cf9 | 186 | static u32 get_fw_base(struct ast_device *ast) |
83c6620b DA |
187 | { |
188 | return ast_mindwm(ast, 0x1e6e2104) & 0x7fffffff; | |
189 | } | |
190 | ||
191 | bool ast_backup_fw(struct drm_device *dev, u8 *addr, u32 size) | |
192 | { | |
5abaa683 | 193 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
194 | u32 i, data; |
195 | u32 boot_address; | |
196 | ||
ba4e0339 KC |
197 | if (ast->config_mode != ast_use_p2a) |
198 | return false; | |
199 | ||
83c6620b DA |
200 | data = ast_mindwm(ast, 0x1e6e2100) & 0x01; |
201 | if (data) { | |
202 | boot_address = get_fw_base(ast); | |
203 | for (i = 0; i < size; i += 4) | |
204 | *(u32 *)(addr + i) = ast_mindwm(ast, boot_address + i); | |
205 | return true; | |
206 | } | |
207 | return false; | |
208 | } | |
209 | ||
12f8030e | 210 | static bool ast_launch_m68k(struct drm_device *dev) |
83c6620b | 211 | { |
5abaa683 | 212 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
213 | u32 i, data, len = 0; |
214 | u32 boot_address; | |
215 | u8 *fw_addr = NULL; | |
216 | u8 jreg; | |
217 | ||
ba4e0339 KC |
218 | if (ast->config_mode != ast_use_p2a) |
219 | return false; | |
220 | ||
83c6620b DA |
221 | data = ast_mindwm(ast, 0x1e6e2100) & 0x01; |
222 | if (!data) { | |
223 | ||
224 | if (ast->dp501_fw_addr) { | |
225 | fw_addr = ast->dp501_fw_addr; | |
226 | len = 32*1024; | |
12f8030e EE |
227 | } else { |
228 | if (!ast->dp501_fw && | |
229 | ast_load_dp501_microcode(dev) < 0) | |
230 | return false; | |
231 | ||
83c6620b DA |
232 | fw_addr = (u8 *)ast->dp501_fw->data; |
233 | len = ast->dp501_fw->size; | |
234 | } | |
235 | /* Get BootAddress */ | |
236 | ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8); | |
237 | data = ast_mindwm(ast, 0x1e6e0004); | |
238 | switch (data & 0x03) { | |
239 | case 0: | |
240 | boot_address = 0x44000000; | |
241 | break; | |
242 | default: | |
243 | case 1: | |
244 | boot_address = 0x48000000; | |
245 | break; | |
246 | case 2: | |
247 | boot_address = 0x50000000; | |
248 | break; | |
249 | case 3: | |
250 | boot_address = 0x60000000; | |
251 | break; | |
252 | } | |
253 | boot_address -= 0x200000; /* -2MB */ | |
254 | ||
255 | /* copy image to buffer */ | |
256 | for (i = 0; i < len; i += 4) { | |
257 | data = *(u32 *)(fw_addr + i); | |
258 | ast_moutdwm(ast, boot_address + i, data); | |
259 | } | |
260 | ||
261 | /* Init SCU */ | |
262 | ast_moutdwm(ast, 0x1e6e2000, 0x1688a8a8); | |
263 | ||
264 | /* Launch FW */ | |
265 | ast_moutdwm(ast, 0x1e6e2104, 0x80000000 + boot_address); | |
266 | ast_moutdwm(ast, 0x1e6e2100, 1); | |
267 | ||
268 | /* Update Scratch */ | |
269 | data = ast_mindwm(ast, 0x1e6e2040) & 0xfffff1ff; /* D[11:9] = 100b: UEFI handling */ | |
270 | data |= 0x800; | |
271 | ast_moutdwm(ast, 0x1e6e2040, data); | |
272 | ||
c79479fa | 273 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0x99, 0xfc); /* D[1:0]: Reserved Video Buffer */ |
83c6620b | 274 | jreg |= 0x02; |
c79479fa | 275 | ast_set_index_reg(ast, AST_IO_VGACRI, 0x99, jreg); |
83c6620b DA |
276 | } |
277 | return true; | |
278 | } | |
279 | ||
f6d9f39f | 280 | static bool ast_dp501_is_connected(struct ast_device *ast) |
83c6620b | 281 | { |
f81bb0ac | 282 | u32 boot_address, offset, data; |
83c6620b | 283 | |
ba4e0339 KC |
284 | if (ast->config_mode == ast_use_p2a) { |
285 | boot_address = get_fw_base(ast); | |
83c6620b | 286 | |
ba4e0339 KC |
287 | /* validate FW version */ |
288 | offset = AST_DP501_GBL_VERSION; | |
289 | data = ast_mindwm(ast, boot_address + offset); | |
290 | if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) | |
291 | return false; | |
292 | ||
293 | /* validate PnP Monitor */ | |
294 | offset = AST_DP501_PNPMONITOR; | |
295 | data = ast_mindwm(ast, boot_address + offset); | |
296 | if (!(data & AST_DP501_PNP_CONNECTED)) | |
297 | return false; | |
ba4e0339 KC |
298 | } else { |
299 | if (!ast->dp501_fw_buf) | |
300 | return false; | |
301 | ||
302 | /* dummy read */ | |
303 | offset = 0x0000; | |
304 | data = readl(ast->dp501_fw_buf + offset); | |
305 | ||
306 | /* validate FW version */ | |
307 | offset = AST_DP501_GBL_VERSION; | |
308 | data = readl(ast->dp501_fw_buf + offset); | |
309 | if ((data & AST_DP501_FW_VERSION_MASK) != AST_DP501_FW_VERSION_1) | |
310 | return false; | |
311 | ||
312 | /* validate PnP Monitor */ | |
313 | offset = AST_DP501_PNPMONITOR; | |
314 | data = readl(ast->dp501_fw_buf + offset); | |
315 | if (!(data & AST_DP501_PNP_CONNECTED)) | |
316 | return false; | |
f81bb0ac JF |
317 | } |
318 | return true; | |
319 | } | |
320 | ||
9e7a74a1 | 321 | static int ast_dp512_read_edid_block(void *data, u8 *buf, unsigned int block, size_t len) |
f81bb0ac | 322 | { |
9e7a74a1 TZ |
323 | struct ast_device *ast = data; |
324 | size_t rdlen = round_up(len, 4); | |
325 | u32 i, boot_address, offset, ediddata; | |
f81bb0ac | 326 | |
9e7a74a1 TZ |
327 | if (block > (512 / EDID_LENGTH)) |
328 | return -EIO; | |
329 | ||
330 | offset = AST_DP501_EDID_DATA + block * EDID_LENGTH; | |
f81bb0ac JF |
331 | |
332 | if (ast->config_mode == ast_use_p2a) { | |
333 | boot_address = get_fw_base(ast); | |
ba4e0339 | 334 | |
9e7a74a1 TZ |
335 | for (i = 0; i < rdlen; i += 4) { |
336 | ediddata = ast_mindwm(ast, boot_address + offset + i); | |
337 | memcpy(buf, &ediddata, min((len - i), 4)); | |
338 | buf += 4; | |
f81bb0ac JF |
339 | } |
340 | } else { | |
9e7a74a1 TZ |
341 | for (i = 0; i < rdlen; i += 4) { |
342 | ediddata = readl(ast->dp501_fw_buf + offset + i); | |
343 | memcpy(buf, &ediddata, min((len - i), 4)); | |
344 | buf += 4; | |
ba4e0339 | 345 | } |
83c6620b DA |
346 | } |
347 | ||
348 | return true; | |
349 | } | |
350 | ||
351 | static bool ast_init_dvo(struct drm_device *dev) | |
352 | { | |
5abaa683 | 353 | struct ast_device *ast = to_ast_device(dev); |
83c6620b DA |
354 | u8 jreg; |
355 | u32 data; | |
356 | ast_write32(ast, 0xf004, 0x1e6e0000); | |
357 | ast_write32(ast, 0xf000, 0x1); | |
358 | ast_write32(ast, 0x12000, 0x1688a8a8); | |
359 | ||
c79479fa | 360 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd0, 0xff); |
83c6620b DA |
361 | if (!(jreg & 0x80)) { |
362 | /* Init SCU DVO Settings */ | |
363 | data = ast_read32(ast, 0x12008); | |
364 | /* delay phase */ | |
365 | data &= 0xfffff8ff; | |
366 | data |= 0x00000500; | |
367 | ast_write32(ast, 0x12008, data); | |
368 | ||
ecf64579 | 369 | if (IS_AST_GEN4(ast)) { |
83c6620b DA |
370 | data = ast_read32(ast, 0x12084); |
371 | /* multi-pins for DVO single-edge */ | |
372 | data |= 0xfffe0000; | |
373 | ast_write32(ast, 0x12084, data); | |
374 | ||
375 | data = ast_read32(ast, 0x12088); | |
376 | /* multi-pins for DVO single-edge */ | |
377 | data |= 0x000fffff; | |
378 | ast_write32(ast, 0x12088, data); | |
379 | ||
380 | data = ast_read32(ast, 0x12090); | |
381 | /* multi-pins for DVO single-edge */ | |
382 | data &= 0xffffffcf; | |
383 | data |= 0x00000020; | |
384 | ast_write32(ast, 0x12090, data); | |
ecf64579 | 385 | } else { /* AST GEN5+ */ |
83c6620b DA |
386 | data = ast_read32(ast, 0x12088); |
387 | /* multi-pins for DVO single-edge */ | |
388 | data |= 0x30000000; | |
389 | ast_write32(ast, 0x12088, data); | |
390 | ||
391 | data = ast_read32(ast, 0x1208c); | |
392 | /* multi-pins for DVO single-edge */ | |
393 | data |= 0x000000cf; | |
394 | ast_write32(ast, 0x1208c, data); | |
395 | ||
396 | data = ast_read32(ast, 0x120a4); | |
397 | /* multi-pins for DVO single-edge */ | |
398 | data |= 0xffff0000; | |
399 | ast_write32(ast, 0x120a4, data); | |
400 | ||
401 | data = ast_read32(ast, 0x120a8); | |
402 | /* multi-pins for DVO single-edge */ | |
403 | data |= 0x0000000f; | |
404 | ast_write32(ast, 0x120a8, data); | |
405 | ||
406 | data = ast_read32(ast, 0x12094); | |
407 | /* multi-pins for DVO single-edge */ | |
408 | data |= 0x00000002; | |
409 | ast_write32(ast, 0x12094, data); | |
410 | } | |
411 | } | |
412 | ||
413 | /* Force to DVO */ | |
414 | data = ast_read32(ast, 0x1202c); | |
415 | data &= 0xfffbffff; | |
416 | ast_write32(ast, 0x1202c, data); | |
417 | ||
418 | /* Init VGA DVO Settings */ | |
c79479fa | 419 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x80); |
83c6620b DA |
420 | return true; |
421 | } | |
422 | ||
37b9b81f BH |
423 | |
424 | static void ast_init_analog(struct drm_device *dev) | |
425 | { | |
5abaa683 | 426 | struct ast_device *ast = to_ast_device(dev); |
37b9b81f BH |
427 | u32 data; |
428 | ||
429 | /* | |
430 | * Set DAC source to VGA mode in SCU2C via the P2A | |
431 | * bridge. First configure the P2U to target the SCU | |
432 | * in case it isn't at this stage. | |
433 | */ | |
434 | ast_write32(ast, 0xf004, 0x1e6e0000); | |
435 | ast_write32(ast, 0xf000, 0x1); | |
436 | ||
437 | /* Then unlock the SCU with the magic password */ | |
438 | ast_write32(ast, 0x12000, 0x1688a8a8); | |
439 | ast_write32(ast, 0x12000, 0x1688a8a8); | |
440 | ast_write32(ast, 0x12000, 0x1688a8a8); | |
441 | ||
442 | /* Finally, clear bits [17:16] of SCU2c */ | |
443 | data = ast_read32(ast, 0x1202c); | |
444 | data &= 0xfffcffff; | |
445 | ast_write32(ast, 0, data); | |
446 | ||
447 | /* Disable DVO */ | |
c79479fa | 448 | ast_set_index_reg_mask(ast, AST_IO_VGACRI, 0xa3, 0xcf, 0x00); |
37b9b81f BH |
449 | } |
450 | ||
83c6620b DA |
451 | void ast_init_3rdtx(struct drm_device *dev) |
452 | { | |
5abaa683 | 453 | struct ast_device *ast = to_ast_device(dev); |
83c6620b | 454 | u8 jreg; |
37b9b81f | 455 | |
ecf64579 | 456 | if (IS_AST_GEN4(ast) || IS_AST_GEN5(ast)) { |
c79479fa | 457 | jreg = ast_get_index_reg_mask(ast, AST_IO_VGACRI, 0xd1, 0xff); |
83c6620b DA |
458 | switch (jreg & 0x0e) { |
459 | case 0x04: | |
460 | ast_init_dvo(dev); | |
461 | break; | |
462 | case 0x08: | |
463 | ast_launch_m68k(dev); | |
464 | break; | |
465 | case 0x0c: | |
466 | ast_init_dvo(dev); | |
467 | break; | |
468 | default: | |
7f35680a | 469 | if (ast->tx_chip_types & BIT(AST_TX_SIL164)) |
83c6620b | 470 | ast_init_dvo(dev); |
37b9b81f BH |
471 | else |
472 | ast_init_analog(dev); | |
83c6620b DA |
473 | } |
474 | } | |
475 | } | |
f6d9f39f TZ |
476 | |
477 | /* | |
478 | * Encoder | |
479 | */ | |
480 | ||
481 | static const struct drm_encoder_funcs ast_dp501_encoder_funcs = { | |
482 | .destroy = drm_encoder_cleanup, | |
483 | }; | |
484 | ||
485 | static void ast_dp501_encoder_helper_atomic_enable(struct drm_encoder *encoder, | |
486 | struct drm_atomic_state *state) | |
487 | { | |
488 | struct drm_device *dev = encoder->dev; | |
489 | ||
490 | ast_set_dp501_video_output(dev, 1); | |
491 | } | |
492 | ||
493 | static void ast_dp501_encoder_helper_atomic_disable(struct drm_encoder *encoder, | |
494 | struct drm_atomic_state *state) | |
495 | { | |
496 | struct drm_device *dev = encoder->dev; | |
497 | ||
498 | ast_set_dp501_video_output(dev, 0); | |
499 | } | |
500 | ||
501 | static const struct drm_encoder_helper_funcs ast_dp501_encoder_helper_funcs = { | |
502 | .atomic_enable = ast_dp501_encoder_helper_atomic_enable, | |
503 | .atomic_disable = ast_dp501_encoder_helper_atomic_disable, | |
504 | }; | |
505 | ||
506 | /* | |
507 | * Connector | |
508 | */ | |
509 | ||
510 | static int ast_dp501_connector_helper_get_modes(struct drm_connector *connector) | |
511 | { | |
44a37ba1 | 512 | struct ast_connector *ast_connector = to_ast_connector(connector); |
f6d9f39f TZ |
513 | int count; |
514 | ||
44a37ba1 TZ |
515 | if (ast_connector->physical_status == connector_status_connected) { |
516 | struct ast_device *ast = to_ast_device(connector->dev); | |
517 | const struct drm_edid *drm_edid; | |
518 | ||
519 | drm_edid = drm_edid_read_custom(connector, ast_dp512_read_edid_block, ast); | |
520 | drm_edid_connector_update(connector, drm_edid); | |
521 | count = drm_edid_connector_add_modes(connector); | |
522 | drm_edid_free(drm_edid); | |
523 | } else { | |
524 | drm_edid_connector_update(connector, NULL); | |
525 | ||
526 | /* | |
527 | * There's no EDID data without a connected monitor. Set BMC- | |
528 | * compatible modes in this case. The XGA default resolution | |
529 | * should work well for all BMCs. | |
530 | */ | |
531 | count = drm_add_modes_noedid(connector, 4096, 4096); | |
532 | if (count) | |
533 | drm_set_preferred_mode(connector, 1024, 768); | |
534 | } | |
f6d9f39f TZ |
535 | |
536 | return count; | |
f6d9f39f TZ |
537 | } |
538 | ||
539 | static int ast_dp501_connector_helper_detect_ctx(struct drm_connector *connector, | |
540 | struct drm_modeset_acquire_ctx *ctx, | |
541 | bool force) | |
542 | { | |
80431c01 | 543 | struct ast_connector *ast_connector = to_ast_connector(connector); |
f6d9f39f | 544 | struct ast_device *ast = to_ast_device(connector->dev); |
80431c01 | 545 | enum drm_connector_status status = connector_status_disconnected; |
f6d9f39f TZ |
546 | |
547 | if (ast_dp501_is_connected(ast)) | |
44a37ba1 | 548 | status = connector_status_connected; |
80431c01 | 549 | |
44a37ba1 TZ |
550 | if (status != ast_connector->physical_status) |
551 | ++connector->epoch_counter; | |
80431c01 TZ |
552 | ast_connector->physical_status = status; |
553 | ||
44a37ba1 | 554 | return connector_status_connected; |
f6d9f39f TZ |
555 | } |
556 | ||
557 | static const struct drm_connector_helper_funcs ast_dp501_connector_helper_funcs = { | |
558 | .get_modes = ast_dp501_connector_helper_get_modes, | |
559 | .detect_ctx = ast_dp501_connector_helper_detect_ctx, | |
560 | }; | |
561 | ||
562 | static const struct drm_connector_funcs ast_dp501_connector_funcs = { | |
563 | .reset = drm_atomic_helper_connector_reset, | |
564 | .fill_modes = drm_helper_probe_single_connector_modes, | |
565 | .destroy = drm_connector_cleanup, | |
566 | .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, | |
567 | .atomic_destroy_state = drm_atomic_helper_connector_destroy_state, | |
568 | }; | |
569 | ||
570 | static int ast_dp501_connector_init(struct drm_device *dev, struct drm_connector *connector) | |
571 | { | |
572 | int ret; | |
573 | ||
574 | ret = drm_connector_init(dev, connector, &ast_dp501_connector_funcs, | |
575 | DRM_MODE_CONNECTOR_DisplayPort); | |
576 | if (ret) | |
577 | return ret; | |
578 | ||
579 | drm_connector_helper_add(connector, &ast_dp501_connector_helper_funcs); | |
580 | ||
581 | connector->interlace_allowed = 0; | |
582 | connector->doublescan_allowed = 0; | |
583 | ||
584 | connector->polled = DRM_CONNECTOR_POLL_CONNECT | DRM_CONNECTOR_POLL_DISCONNECT; | |
585 | ||
586 | return 0; | |
587 | } | |
588 | ||
589 | int ast_dp501_output_init(struct ast_device *ast) | |
590 | { | |
591 | struct drm_device *dev = &ast->base; | |
592 | struct drm_crtc *crtc = &ast->crtc; | |
593 | struct drm_encoder *encoder = &ast->output.dp501.encoder; | |
80431c01 TZ |
594 | struct ast_connector *ast_connector = &ast->output.dp501.connector; |
595 | struct drm_connector *connector = &ast_connector->base; | |
f6d9f39f TZ |
596 | int ret; |
597 | ||
598 | ret = drm_encoder_init(dev, encoder, &ast_dp501_encoder_funcs, | |
599 | DRM_MODE_ENCODER_TMDS, NULL); | |
600 | if (ret) | |
601 | return ret; | |
602 | drm_encoder_helper_add(encoder, &ast_dp501_encoder_helper_funcs); | |
603 | ||
604 | encoder->possible_crtcs = drm_crtc_mask(crtc); | |
605 | ||
606 | ret = ast_dp501_connector_init(dev, connector); | |
607 | if (ret) | |
608 | return ret; | |
80431c01 | 609 | ast_connector->physical_status = connector->status; |
f6d9f39f TZ |
610 | |
611 | ret = drm_connector_attach_encoder(connector, encoder); | |
612 | if (ret) | |
613 | return ret; | |
614 | ||
615 | return 0; | |
616 | } |