xref: /openbmc/linux/drivers/gpu/drm/msm/dp/dp_drm.c (revision 335f70fa)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
4  */
5 
6 #include <drm/drm_atomic_helper.h>
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_bridge.h>
9 #include <drm/drm_crtc.h>
10 
11 #include "msm_drv.h"
12 #include "msm_kms.h"
13 #include "dp_drm.h"
14 
15 
16 struct msm_dp_bridge {
17 	struct drm_bridge bridge;
18 	struct msm_dp *dp_display;
19 };
20 
21 #define to_dp_display(x)     container_of((x), struct msm_dp_bridge, bridge)
22 
23 struct dp_connector {
24 	struct drm_connector base;
25 	struct msm_dp *dp_display;
26 };
27 #define to_dp_connector(x) container_of(x, struct dp_connector, base)
28 
29 /**
30  * dp_connector_detect - callback to determine if connector is connected
31  * @conn: Pointer to drm connector structure
32  * @force: Force detect setting from drm framework
33  * Returns: Connector 'is connected' status
34  */
35 static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
36 		bool force)
37 {
38 	struct msm_dp *dp;
39 
40 	dp = to_dp_connector(conn)->dp_display;
41 
42 	DRM_DEBUG_DP("is_connected = %s\n",
43 		(dp->is_connected) ? "true" : "false");
44 
45 	return (dp->is_connected) ? connector_status_connected :
46 					connector_status_disconnected;
47 }
48 
49 /**
50  * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
51  * @connector: Pointer to drm connector structure
52  * Returns: Number of modes added
53  */
54 static int dp_connector_get_modes(struct drm_connector *connector)
55 {
56 	int rc = 0;
57 	struct msm_dp *dp;
58 	struct dp_display_mode *dp_mode = NULL;
59 	struct drm_display_mode *m, drm_mode;
60 
61 	if (!connector)
62 		return 0;
63 
64 	dp = to_dp_connector(connector)->dp_display;
65 
66 	dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
67 	if (!dp_mode)
68 		return 0;
69 
70 	/* pluggable case assumes EDID is read when HPD */
71 	if (dp->is_connected) {
72 		/*
73 		 *The get_modes() function might return one mode that is stored
74 		 * in dp_mode when compliance test is in progress. If not, the
75 		 * return value is equal to the total number of modes supported
76 		 * by the sink
77 		 */
78 		rc = dp_display_get_modes(dp, dp_mode);
79 		if (rc <= 0) {
80 			DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
81 			kfree(dp_mode);
82 			return rc;
83 		}
84 		if (dp_mode->drm_mode.clock) { /* valid DP mode */
85 			memset(&drm_mode, 0x0, sizeof(drm_mode));
86 			drm_mode_copy(&drm_mode, &dp_mode->drm_mode);
87 			m = drm_mode_duplicate(connector->dev, &drm_mode);
88 			if (!m) {
89 				DRM_ERROR("failed to add mode %ux%u\n",
90 				       drm_mode.hdisplay,
91 				       drm_mode.vdisplay);
92 				kfree(dp_mode);
93 				return 0;
94 			}
95 			drm_mode_probed_add(connector, m);
96 		}
97 	} else {
98 		DRM_DEBUG_DP("No sink connected\n");
99 	}
100 	kfree(dp_mode);
101 	return rc;
102 }
103 
104 /**
105  * dp_connector_mode_valid - callback to determine if specified mode is valid
106  * @connector: Pointer to drm connector structure
107  * @mode: Pointer to drm mode structure
108  * Returns: Validity status for specified mode
109  */
110 static enum drm_mode_status dp_connector_mode_valid(
111 		struct drm_connector *connector,
112 		struct drm_display_mode *mode)
113 {
114 	struct msm_dp *dp_disp;
115 
116 	dp_disp = to_dp_connector(connector)->dp_display;
117 
118 	if ((dp_disp->max_pclk_khz <= 0) ||
119 			(dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) ||
120 			(mode->clock > dp_disp->max_pclk_khz))
121 		return MODE_BAD;
122 
123 	return dp_display_validate_mode(dp_disp, mode->clock);
124 }
125 
126 static const struct drm_connector_funcs dp_connector_funcs = {
127 	.detect = dp_connector_detect,
128 	.fill_modes = drm_helper_probe_single_connector_modes,
129 	.destroy = drm_connector_cleanup,
130 	.reset = drm_atomic_helper_connector_reset,
131 	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
132 	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
133 };
134 
135 static const struct drm_connector_helper_funcs dp_connector_helper_funcs = {
136 	.get_modes = dp_connector_get_modes,
137 	.mode_valid = dp_connector_mode_valid,
138 };
139 
140 /* connector initialization */
141 struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
142 {
143 	struct drm_connector *connector = NULL;
144 	struct dp_connector *dp_connector;
145 	int ret;
146 
147 	dp_connector = devm_kzalloc(dp_display->drm_dev->dev,
148 					sizeof(*dp_connector),
149 					GFP_KERNEL);
150 	if (!dp_connector)
151 		return ERR_PTR(-ENOMEM);
152 
153 	dp_connector->dp_display = dp_display;
154 
155 	connector = &dp_connector->base;
156 
157 	ret = drm_connector_init(dp_display->drm_dev, connector,
158 			&dp_connector_funcs,
159 			dp_display->connector_type);
160 	if (ret)
161 		return ERR_PTR(ret);
162 
163 	drm_connector_helper_add(connector, &dp_connector_helper_funcs);
164 
165 	/*
166 	 * Enable HPD to let hpd event is handled when cable is connected.
167 	 */
168 	connector->polled = DRM_CONNECTOR_POLL_HPD;
169 
170 	drm_connector_attach_encoder(connector, dp_display->encoder);
171 
172 	return connector;
173 }
174 
175 static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
176 				const struct drm_display_mode *mode,
177 				const struct drm_display_mode *adjusted_mode)
178 {
179 	struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
180 	struct msm_dp *dp_display = dp_bridge->dp_display;
181 
182 	msm_dp_display_mode_set(dp_display, drm_bridge->encoder, mode, adjusted_mode);
183 }
184 
185 static void dp_bridge_enable(struct drm_bridge *drm_bridge)
186 {
187 	struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
188 	struct msm_dp *dp_display = dp_bridge->dp_display;
189 
190 	msm_dp_display_enable(dp_display, drm_bridge->encoder);
191 }
192 
193 static void dp_bridge_disable(struct drm_bridge *drm_bridge)
194 {
195 	struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
196 	struct msm_dp *dp_display = dp_bridge->dp_display;
197 
198 	msm_dp_display_pre_disable(dp_display, drm_bridge->encoder);
199 }
200 
201 static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
202 {
203 	struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
204 	struct msm_dp *dp_display = dp_bridge->dp_display;
205 
206 	msm_dp_display_disable(dp_display, drm_bridge->encoder);
207 }
208 
209 static const struct drm_bridge_funcs dp_bridge_ops = {
210 	.enable       = dp_bridge_enable,
211 	.disable      = dp_bridge_disable,
212 	.post_disable = dp_bridge_post_disable,
213 	.mode_set     = dp_bridge_mode_set,
214 };
215 
216 struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
217 			struct drm_encoder *encoder)
218 {
219 	int rc;
220 	struct msm_dp_bridge *dp_bridge;
221 	struct drm_bridge *bridge;
222 
223 	dp_bridge = devm_kzalloc(dev->dev, sizeof(*dp_bridge), GFP_KERNEL);
224 	if (!dp_bridge)
225 		return ERR_PTR(-ENOMEM);
226 
227 	dp_bridge->dp_display = dp_display;
228 
229 	bridge = &dp_bridge->bridge;
230 	bridge->funcs = &dp_bridge_ops;
231 	bridge->encoder = encoder;
232 
233 	rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
234 	if (rc) {
235 		DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
236 		return ERR_PTR(rc);
237 	}
238 
239 	if (dp_display->next_bridge) {
240 		rc = drm_bridge_attach(dp_display->encoder,
241 					dp_display->next_bridge, bridge,
242 					DRM_BRIDGE_ATTACH_NO_CONNECTOR);
243 		if (rc < 0) {
244 			DRM_ERROR("failed to attach panel bridge: %d\n", rc);
245 			drm_bridge_remove(bridge);
246 			return ERR_PTR(rc);
247 		}
248 	}
249 
250 	return bridge;
251 }
252