xref: /openbmc/linux/drivers/gpu/drm/ast/ast_dp.c (revision a81b2acd)
1594e9c04SKuoHsiang Chou // SPDX-License-Identifier: GPL-2.0
2594e9c04SKuoHsiang Chou // Copyright (c) 2021, ASPEED Technology Inc.
3594e9c04SKuoHsiang Chou // Authors: KuoHsiang Chou <kuohsiang_chou@aspeedtech.com>
4594e9c04SKuoHsiang Chou 
5594e9c04SKuoHsiang Chou #include <linux/firmware.h>
6594e9c04SKuoHsiang Chou #include <linux/delay.h>
7594e9c04SKuoHsiang Chou #include <drm/drm_print.h>
8594e9c04SKuoHsiang Chou #include "ast_drv.h"
9594e9c04SKuoHsiang Chou 
ast_astdp_is_connected(struct ast_device * ast)10f81bb0acSJocelyn Falempe bool ast_astdp_is_connected(struct ast_device *ast)
11f81bb0acSJocelyn Falempe {
12f81bb0acSJocelyn Falempe 	if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING))
13f81bb0acSJocelyn Falempe 		return false;
14f81bb0acSJocelyn Falempe 	if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))
15f81bb0acSJocelyn Falempe 		return false;
16f81bb0acSJocelyn Falempe 	if (!ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS))
17f81bb0acSJocelyn Falempe 		return false;
18f81bb0acSJocelyn Falempe 	return true;
19f81bb0acSJocelyn Falempe }
20f81bb0acSJocelyn Falempe 
ast_astdp_read_edid(struct drm_device * dev,u8 * ediddata)21594e9c04SKuoHsiang Chou int ast_astdp_read_edid(struct drm_device *dev, u8 *ediddata)
22594e9c04SKuoHsiang Chou {
235abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
24594e9c04SKuoHsiang Chou 	u8 i = 0, j = 0;
25594e9c04SKuoHsiang Chou 
26594e9c04SKuoHsiang Chou 	/*
27594e9c04SKuoHsiang Chou 	 * CRD1[b5]: DP MCU FW is executing
28594e9c04SKuoHsiang Chou 	 * CRDC[b0]: DP link success
29594e9c04SKuoHsiang Chou 	 * CRDF[b0]: DP HPD
30594e9c04SKuoHsiang Chou 	 * CRE5[b0]: Host reading EDID process is done
31594e9c04SKuoHsiang Chou 	 */
32594e9c04SKuoHsiang Chou 	if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) &&
33594e9c04SKuoHsiang Chou 		ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
34594e9c04SKuoHsiang Chou 		ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD) &&
35594e9c04SKuoHsiang Chou 		ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
36594e9c04SKuoHsiang Chou 								ASTDP_HOST_EDID_READ_DONE_MASK))) {
37594e9c04SKuoHsiang Chou 		goto err_astdp_edid_not_ready;
38594e9c04SKuoHsiang Chou 	}
39594e9c04SKuoHsiang Chou 
40594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
41594e9c04SKuoHsiang Chou 							0x00);
42594e9c04SKuoHsiang Chou 
43594e9c04SKuoHsiang Chou 	for (i = 0; i < 32; i++) {
44594e9c04SKuoHsiang Chou 		/*
45594e9c04SKuoHsiang Chou 		 * CRE4[7:0]: Read-Pointer for EDID (Unit: 4bytes); valid range: 0~64
46594e9c04SKuoHsiang Chou 		 */
47594e9c04SKuoHsiang Chou 		ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE4,
48232b95baSKuoHsiang Chou 				       ASTDP_AND_CLEAR_MASK, (u8)i);
49594e9c04SKuoHsiang Chou 		j = 0;
50594e9c04SKuoHsiang Chou 
51594e9c04SKuoHsiang Chou 		/*
52594e9c04SKuoHsiang Chou 		 * CRD7[b0]: valid flag for EDID
53594e9c04SKuoHsiang Chou 		 * CRD6[b0]: mirror read pointer for EDID
54594e9c04SKuoHsiang Chou 		 */
55594e9c04SKuoHsiang Chou 		while ((ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD7,
56594e9c04SKuoHsiang Chou 				ASTDP_EDID_VALID_FLAG_MASK) != 0x01) ||
57594e9c04SKuoHsiang Chou 			(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD6,
58594e9c04SKuoHsiang Chou 						ASTDP_EDID_READ_POINTER_MASK) != i)) {
59594e9c04SKuoHsiang Chou 			/*
60594e9c04SKuoHsiang Chou 			 * Delay are getting longer with each retry.
61594e9c04SKuoHsiang Chou 			 * 1. The Delays are often 2 loops when users request "Display Settings"
62594e9c04SKuoHsiang Chou 			 *	  of right-click of mouse.
63594e9c04SKuoHsiang Chou 			 * 2. The Delays are often longer a lot when system resume from S3/S4.
64594e9c04SKuoHsiang Chou 			 */
65594e9c04SKuoHsiang Chou 			mdelay(j+1);
66594e9c04SKuoHsiang Chou 
67594e9c04SKuoHsiang Chou 			if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1,
68594e9c04SKuoHsiang Chou 							ASTDP_MCU_FW_EXECUTING) &&
69594e9c04SKuoHsiang Chou 				ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC,
70594e9c04SKuoHsiang Chou 							ASTDP_LINK_SUCCESS) &&
71594e9c04SKuoHsiang Chou 				ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD))) {
72594e9c04SKuoHsiang Chou 				goto err_astdp_jump_out_loop_of_edid;
73594e9c04SKuoHsiang Chou 			}
74594e9c04SKuoHsiang Chou 
75594e9c04SKuoHsiang Chou 			j++;
76594e9c04SKuoHsiang Chou 			if (j > 200)
77594e9c04SKuoHsiang Chou 				goto err_astdp_jump_out_loop_of_edid;
78594e9c04SKuoHsiang Chou 		}
79594e9c04SKuoHsiang Chou 
80594e9c04SKuoHsiang Chou 		*(ediddata) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT,
81594e9c04SKuoHsiang Chou 							0xD8, ASTDP_EDID_READ_DATA_MASK);
82594e9c04SKuoHsiang Chou 		*(ediddata + 1) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD9,
83594e9c04SKuoHsiang Chou 								ASTDP_EDID_READ_DATA_MASK);
84594e9c04SKuoHsiang Chou 		*(ediddata + 2) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDA,
85594e9c04SKuoHsiang Chou 								ASTDP_EDID_READ_DATA_MASK);
86594e9c04SKuoHsiang Chou 		*(ediddata + 3) = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDB,
87594e9c04SKuoHsiang Chou 								ASTDP_EDID_READ_DATA_MASK);
88594e9c04SKuoHsiang Chou 
89594e9c04SKuoHsiang Chou 		if (i == 31) {
90594e9c04SKuoHsiang Chou 			/*
91594e9c04SKuoHsiang Chou 			 * For 128-bytes EDID_1.3,
92594e9c04SKuoHsiang Chou 			 * 1. Add the value of Bytes-126 to Bytes-127.
93594e9c04SKuoHsiang Chou 			 *		The Bytes-127 is Checksum. Sum of all 128bytes should
94594e9c04SKuoHsiang Chou 			 *		equal 0	(mod 256).
95594e9c04SKuoHsiang Chou 			 * 2. Modify Bytes-126 to be 0.
96594e9c04SKuoHsiang Chou 			 *		The Bytes-126 indicates the Number of extensions to
97594e9c04SKuoHsiang Chou 			 *		follow. 0 represents noextensions.
98594e9c04SKuoHsiang Chou 			 */
99594e9c04SKuoHsiang Chou 			*(ediddata + 3) = *(ediddata + 3) + *(ediddata + 2);
100594e9c04SKuoHsiang Chou 			*(ediddata + 2) = 0;
101594e9c04SKuoHsiang Chou 		}
102594e9c04SKuoHsiang Chou 
103594e9c04SKuoHsiang Chou 		ediddata += 4;
104594e9c04SKuoHsiang Chou 	}
105594e9c04SKuoHsiang Chou 
106594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
107594e9c04SKuoHsiang Chou 							ASTDP_HOST_EDID_READ_DONE);
108594e9c04SKuoHsiang Chou 
109594e9c04SKuoHsiang Chou 	return 0;
110594e9c04SKuoHsiang Chou 
111594e9c04SKuoHsiang Chou err_astdp_jump_out_loop_of_edid:
112594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
113594e9c04SKuoHsiang Chou 							(u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
114594e9c04SKuoHsiang Chou 							ASTDP_HOST_EDID_READ_DONE);
115594e9c04SKuoHsiang Chou 	return (~(j+256) + 1);
116594e9c04SKuoHsiang Chou 
117594e9c04SKuoHsiang Chou err_astdp_edid_not_ready:
118594e9c04SKuoHsiang Chou 	if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING)))
119594e9c04SKuoHsiang Chou 		return (~0xD1 + 1);
120594e9c04SKuoHsiang Chou 	if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS)))
121594e9c04SKuoHsiang Chou 		return (~0xDC + 1);
122594e9c04SKuoHsiang Chou 	if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)))
123594e9c04SKuoHsiang Chou 		return (~0xDF + 1);
124594e9c04SKuoHsiang Chou 	if (!(ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5, ASTDP_HOST_EDID_READ_DONE_MASK)))
125594e9c04SKuoHsiang Chou 		return (~0xE5 + 1);
126594e9c04SKuoHsiang Chou 
127594e9c04SKuoHsiang Chou 	return	0;
128594e9c04SKuoHsiang Chou }
129594e9c04SKuoHsiang Chou 
130594e9c04SKuoHsiang Chou /*
131594e9c04SKuoHsiang Chou  * Launch Aspeed DP
132594e9c04SKuoHsiang Chou  */
ast_dp_launch(struct drm_device * dev)133bed61c8fSJammy Huang void ast_dp_launch(struct drm_device *dev)
134594e9c04SKuoHsiang Chou {
135bed61c8fSJammy Huang 	u32 i = 0;
136594e9c04SKuoHsiang Chou 	u8 bDPExecute = 1;
1375abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
138594e9c04SKuoHsiang Chou 
139594e9c04SKuoHsiang Chou 	// Wait one second then timeout.
140bed61c8fSJammy Huang 	while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xD1, ASTDP_MCU_FW_EXECUTING) !=
141bed61c8fSJammy Huang 		ASTDP_MCU_FW_EXECUTING) {
142594e9c04SKuoHsiang Chou 		i++;
143594e9c04SKuoHsiang Chou 		// wait 100 ms
144594e9c04SKuoHsiang Chou 		msleep(100);
145594e9c04SKuoHsiang Chou 
146594e9c04SKuoHsiang Chou 		if (i >= 10) {
147594e9c04SKuoHsiang Chou 			// DP would not be ready.
148594e9c04SKuoHsiang Chou 			bDPExecute = 0;
149594e9c04SKuoHsiang Chou 			break;
150594e9c04SKuoHsiang Chou 		}
151594e9c04SKuoHsiang Chou 	}
152594e9c04SKuoHsiang Chou 
153bed61c8fSJammy Huang 	if (!bDPExecute)
154bed61c8fSJammy Huang 		drm_err(dev, "Wait DPMCU executing timeout\n");
155594e9c04SKuoHsiang Chou 
156594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE5,
157594e9c04SKuoHsiang Chou 			       (u8) ~ASTDP_HOST_EDID_READ_DONE_MASK,
158594e9c04SKuoHsiang Chou 			       ASTDP_HOST_EDID_READ_DONE);
1597f35680aSThomas Zimmermann }
160594e9c04SKuoHsiang Chou 
161594e9c04SKuoHsiang Chou 
162594e9c04SKuoHsiang Chou 
ast_dp_power_on_off(struct drm_device * dev,bool on)163594e9c04SKuoHsiang Chou void ast_dp_power_on_off(struct drm_device *dev, bool on)
164594e9c04SKuoHsiang Chou {
1655abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
166594e9c04SKuoHsiang Chou 	// Read and Turn off DP PHY sleep
167594e9c04SKuoHsiang Chou 	u8 bE3 = ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, AST_DP_VIDEO_ENABLE);
168594e9c04SKuoHsiang Chou 
169594e9c04SKuoHsiang Chou 	// Turn on DP PHY sleep
170594e9c04SKuoHsiang Chou 	if (!on)
171594e9c04SKuoHsiang Chou 		bE3 |= AST_DP_PHY_SLEEP;
172594e9c04SKuoHsiang Chou 
173594e9c04SKuoHsiang Chou 	// DP Power on/off
174594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_PHY_SLEEP, bE3);
175594e9c04SKuoHsiang Chou }
176594e9c04SKuoHsiang Chou 
177594e9c04SKuoHsiang Chou 
178594e9c04SKuoHsiang Chou 
ast_dp_set_on_off(struct drm_device * dev,bool on)179594e9c04SKuoHsiang Chou void ast_dp_set_on_off(struct drm_device *dev, bool on)
180594e9c04SKuoHsiang Chou {
1815abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(dev);
182594e9c04SKuoHsiang Chou 	u8 video_on_off = on;
183a81b2acdSJammy Huang 	u32 i = 0;
184594e9c04SKuoHsiang Chou 
185594e9c04SKuoHsiang Chou 	// Video On/Off
186594e9c04SKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE3, (u8) ~AST_DP_VIDEO_ENABLE, on);
187594e9c04SKuoHsiang Chou 
188594e9c04SKuoHsiang Chou 	// If DP plug in and link successful then check video on / off status
189594e9c04SKuoHsiang Chou 	if (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDC, ASTDP_LINK_SUCCESS) &&
190594e9c04SKuoHsiang Chou 		ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF, ASTDP_HPD)) {
191594e9c04SKuoHsiang Chou 		video_on_off <<= 4;
192594e9c04SKuoHsiang Chou 		while (ast_get_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xDF,
193594e9c04SKuoHsiang Chou 						ASTDP_MIRROR_VIDEO_ENABLE) != video_on_off) {
194594e9c04SKuoHsiang Chou 			// wait 1 ms
195594e9c04SKuoHsiang Chou 			mdelay(1);
196a81b2acdSJammy Huang 			if (++i > 200)
197a81b2acdSJammy Huang 				break;
198594e9c04SKuoHsiang Chou 		}
199594e9c04SKuoHsiang Chou 	}
200594e9c04SKuoHsiang Chou }
201594e9c04SKuoHsiang Chou 
ast_dp_set_mode(struct drm_crtc * crtc,struct ast_vbios_mode_info * vbios_mode)202594e9c04SKuoHsiang Chou void ast_dp_set_mode(struct drm_crtc *crtc, struct ast_vbios_mode_info *vbios_mode)
203594e9c04SKuoHsiang Chou {
2045abaa683SThomas Zimmermann 	struct ast_device *ast = to_ast_device(crtc->dev);
205594e9c04SKuoHsiang Chou 
206594e9c04SKuoHsiang Chou 	u32 ulRefreshRateIndex;
207594e9c04SKuoHsiang Chou 	u8 ModeIdx;
208594e9c04SKuoHsiang Chou 
209594e9c04SKuoHsiang Chou 	ulRefreshRateIndex = vbios_mode->enh_table->refresh_rate_index - 1;
210594e9c04SKuoHsiang Chou 
211594e9c04SKuoHsiang Chou 	switch (crtc->mode.crtc_hdisplay) {
212594e9c04SKuoHsiang Chou 	case 320:
213594e9c04SKuoHsiang Chou 		ModeIdx = ASTDP_320x240_60;
214594e9c04SKuoHsiang Chou 		break;
215594e9c04SKuoHsiang Chou 	case 400:
216594e9c04SKuoHsiang Chou 		ModeIdx = ASTDP_400x300_60;
217594e9c04SKuoHsiang Chou 		break;
218594e9c04SKuoHsiang Chou 	case 512:
219594e9c04SKuoHsiang Chou 		ModeIdx = ASTDP_512x384_60;
220594e9c04SKuoHsiang Chou 		break;
221594e9c04SKuoHsiang Chou 	case 640:
222594e9c04SKuoHsiang Chou 		ModeIdx = (ASTDP_640x480_60 + (u8) ulRefreshRateIndex);
223594e9c04SKuoHsiang Chou 		break;
224594e9c04SKuoHsiang Chou 	case 800:
225594e9c04SKuoHsiang Chou 		ModeIdx = (ASTDP_800x600_56 + (u8) ulRefreshRateIndex);
226594e9c04SKuoHsiang Chou 		break;
227594e9c04SKuoHsiang Chou 	case 1024:
228594e9c04SKuoHsiang Chou 		ModeIdx = (ASTDP_1024x768_60 + (u8) ulRefreshRateIndex);
229594e9c04SKuoHsiang Chou 		break;
230594e9c04SKuoHsiang Chou 	case 1152:
231594e9c04SKuoHsiang Chou 		ModeIdx = ASTDP_1152x864_75;
232594e9c04SKuoHsiang Chou 		break;
233594e9c04SKuoHsiang Chou 	case 1280:
234594e9c04SKuoHsiang Chou 		if (crtc->mode.crtc_vdisplay == 800)
235594e9c04SKuoHsiang Chou 			ModeIdx = (ASTDP_1280x800_60_RB - (u8) ulRefreshRateIndex);
236594e9c04SKuoHsiang Chou 		else		// 1024
237594e9c04SKuoHsiang Chou 			ModeIdx = (ASTDP_1280x1024_60 + (u8) ulRefreshRateIndex);
238594e9c04SKuoHsiang Chou 		break;
239594e9c04SKuoHsiang Chou 	case 1360:
240594e9c04SKuoHsiang Chou 	case 1366:
241594e9c04SKuoHsiang Chou 		ModeIdx = ASTDP_1366x768_60;
242594e9c04SKuoHsiang Chou 		break;
243594e9c04SKuoHsiang Chou 	case 1440:
244594e9c04SKuoHsiang Chou 		ModeIdx = (ASTDP_1440x900_60_RB - (u8) ulRefreshRateIndex);
245594e9c04SKuoHsiang Chou 		break;
246594e9c04SKuoHsiang Chou 	case 1600:
247594e9c04SKuoHsiang Chou 		if (crtc->mode.crtc_vdisplay == 900)
248594e9c04SKuoHsiang Chou 			ModeIdx = (ASTDP_1600x900_60_RB - (u8) ulRefreshRateIndex);
249594e9c04SKuoHsiang Chou 		else		//1200
250594e9c04SKuoHsiang Chou 			ModeIdx = ASTDP_1600x1200_60;
251594e9c04SKuoHsiang Chou 		break;
252594e9c04SKuoHsiang Chou 	case 1680:
253594e9c04SKuoHsiang Chou 		ModeIdx = (ASTDP_1680x1050_60_RB - (u8) ulRefreshRateIndex);
254594e9c04SKuoHsiang Chou 		break;
255594e9c04SKuoHsiang Chou 	case 1920:
256594e9c04SKuoHsiang Chou 		if (crtc->mode.crtc_vdisplay == 1080)
257594e9c04SKuoHsiang Chou 			ModeIdx = ASTDP_1920x1080_60;
258594e9c04SKuoHsiang Chou 		else		//1200
259594e9c04SKuoHsiang Chou 			ModeIdx = ASTDP_1920x1200_60;
260594e9c04SKuoHsiang Chou 		break;
261594e9c04SKuoHsiang Chou 	default:
262594e9c04SKuoHsiang Chou 		return;
263594e9c04SKuoHsiang Chou 	}
264594e9c04SKuoHsiang Chou 
265594e9c04SKuoHsiang Chou 	/*
266594e9c04SKuoHsiang Chou 	 * CRE0[7:0]: MISC0 ((0x00: 18-bpp) or (0x20: 24-bpp)
267594e9c04SKuoHsiang Chou 	 * CRE1[7:0]: MISC1 (default: 0x00)
268594e9c04SKuoHsiang Chou 	 * CRE2[7:0]: video format index (0x00 ~ 0x20 or 0x40 ~ 0x50)
269594e9c04SKuoHsiang Chou 	 */
270232b95baSKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE0, ASTDP_AND_CLEAR_MASK,
271594e9c04SKuoHsiang Chou 			       ASTDP_MISC0_24bpp);
272232b95baSKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE1, ASTDP_AND_CLEAR_MASK, ASTDP_MISC1);
273232b95baSKuoHsiang Chou 	ast_set_index_reg_mask(ast, AST_IO_CRTC_PORT, 0xE2, ASTDP_AND_CLEAR_MASK, ModeIdx);
274594e9c04SKuoHsiang Chou }
275