Commit | Line | Data |
---|---|---|
c943b494 CU |
1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* | |
3 | * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved. | |
4 | */ | |
5 | ||
6 | #include <linux/module.h> | |
7 | #include <linux/slab.h> | |
8 | #include <linux/uaccess.h> | |
9 | #include <linux/debugfs.h> | |
10 | #include <linux/component.h> | |
11 | #include <linux/of_irq.h> | |
8ede2ecc | 12 | #include <linux/delay.h> |
c943b494 CU |
13 | |
14 | #include "msm_drv.h" | |
15 | #include "msm_kms.h" | |
16 | #include "dp_hpd.h" | |
17 | #include "dp_parser.h" | |
18 | #include "dp_power.h" | |
19 | #include "dp_catalog.h" | |
20 | #include "dp_aux.h" | |
220b856a | 21 | #include "dp_reg.h" |
c943b494 CU |
22 | #include "dp_link.h" |
23 | #include "dp_panel.h" | |
24 | #include "dp_ctrl.h" | |
25 | #include "dp_display.h" | |
26 | #include "dp_drm.h" | |
d13e36d7 | 27 | #include "dp_audio.h" |
d11a9369 | 28 | #include "dp_debug.h" |
c943b494 CU |
29 | |
30 | static struct msm_dp *g_dp_display; | |
31 | #define HPD_STRING_SIZE 30 | |
32 | ||
8ede2ecc KH |
33 | enum { |
34 | ISR_DISCONNECTED, | |
35 | ISR_CONNECT_PENDING, | |
36 | ISR_CONNECTED, | |
37 | ISR_HPD_REPLUG_COUNT, | |
38 | ISR_IRQ_HPD_PULSE_COUNT, | |
39 | ISR_HPD_LO_GLITH_COUNT, | |
40 | }; | |
41 | ||
42 | /* event thread connection state */ | |
43 | enum { | |
44 | ST_DISCONNECTED, | |
45 | ST_CONNECT_PENDING, | |
46 | ST_CONNECTED, | |
47 | ST_DISCONNECT_PENDING, | |
62671d2e | 48 | ST_DISPLAY_OFF, |
8ede2ecc KH |
49 | ST_SUSPENDED, |
50 | }; | |
51 | ||
52 | enum { | |
53 | EV_NO_EVENT, | |
54 | /* hpd events */ | |
55 | EV_HPD_INIT_SETUP, | |
56 | EV_HPD_PLUG_INT, | |
57 | EV_IRQ_HPD_INT, | |
58 | EV_HPD_REPLUG_INT, | |
59 | EV_HPD_UNPLUG_INT, | |
60 | EV_USER_NOTIFICATION, | |
61 | EV_CONNECT_PENDING_TIMEOUT, | |
62 | EV_DISCONNECT_PENDING_TIMEOUT, | |
63 | }; | |
64 | ||
65 | #define EVENT_TIMEOUT (HZ/10) /* 100ms */ | |
66 | #define DP_EVENT_Q_MAX 8 | |
67 | ||
68 | #define DP_TIMEOUT_5_SECOND (5000/EVENT_TIMEOUT) | |
69 | #define DP_TIMEOUT_NONE 0 | |
70 | ||
71 | #define WAIT_FOR_RESUME_TIMEOUT_JIFFIES (HZ / 2) | |
72 | ||
73 | struct dp_event { | |
74 | u32 event_id; | |
75 | u32 data; | |
76 | u32 delay; | |
77 | }; | |
78 | ||
c943b494 CU |
79 | struct dp_display_private { |
80 | char *name; | |
81 | int irq; | |
82 | ||
83 | /* state variables */ | |
84 | bool core_initialized; | |
c943b494 CU |
85 | bool hpd_irq_on; |
86 | bool audio_supported; | |
87 | ||
88 | struct platform_device *pdev; | |
89 | struct dentry *root; | |
c943b494 CU |
90 | |
91 | struct dp_usbpd *usbpd; | |
92 | struct dp_parser *parser; | |
93 | struct dp_power *power; | |
94 | struct dp_catalog *catalog; | |
95 | struct drm_dp_aux *aux; | |
96 | struct dp_link *link; | |
97 | struct dp_panel *panel; | |
98 | struct dp_ctrl *ctrl; | |
8ede2ecc | 99 | struct dp_debug *debug; |
c943b494 CU |
100 | |
101 | struct dp_usbpd_cb usbpd_cb; | |
102 | struct dp_display_mode dp_mode; | |
103 | struct msm_dp dp_display; | |
220b856a | 104 | |
158b9aa7 AK |
105 | /* wait for audio signaling */ |
106 | struct completion audio_comp; | |
107 | ||
8ede2ecc KH |
108 | /* event related only access by event thread */ |
109 | struct mutex event_mutex; | |
110 | wait_queue_head_t event_q; | |
19e52bcb | 111 | u32 hpd_state; |
8ede2ecc KH |
112 | u32 event_pndx; |
113 | u32 event_gndx; | |
114 | struct dp_event event_list[DP_EVENT_Q_MAX]; | |
115 | spinlock_t event_lock; | |
116 | ||
d13e36d7 | 117 | struct dp_audio *audio; |
c943b494 CU |
118 | }; |
119 | ||
120 | static const struct of_device_id dp_dt_match[] = { | |
121 | {.compatible = "qcom,sc7180-dp"}, | |
122 | {} | |
123 | }; | |
124 | ||
8ede2ecc KH |
125 | static int dp_add_event(struct dp_display_private *dp_priv, u32 event, |
126 | u32 data, u32 delay) | |
c943b494 | 127 | { |
8ede2ecc KH |
128 | unsigned long flag; |
129 | struct dp_event *todo; | |
130 | int pndx; | |
131 | ||
132 | spin_lock_irqsave(&dp_priv->event_lock, flag); | |
133 | pndx = dp_priv->event_pndx + 1; | |
134 | pndx %= DP_EVENT_Q_MAX; | |
135 | if (pndx == dp_priv->event_gndx) { | |
136 | pr_err("event_q is full: pndx=%d gndx=%d\n", | |
137 | dp_priv->event_pndx, dp_priv->event_gndx); | |
138 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
139 | return -EPERM; | |
220b856a | 140 | } |
8ede2ecc KH |
141 | todo = &dp_priv->event_list[dp_priv->event_pndx++]; |
142 | dp_priv->event_pndx %= DP_EVENT_Q_MAX; | |
143 | todo->event_id = event; | |
144 | todo->data = data; | |
145 | todo->delay = delay; | |
146 | wake_up(&dp_priv->event_q); | |
147 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
c943b494 | 148 | |
8ede2ecc | 149 | return 0; |
220b856a TS |
150 | } |
151 | ||
8ede2ecc | 152 | static int dp_del_event(struct dp_display_private *dp_priv, u32 event) |
220b856a | 153 | { |
8ede2ecc KH |
154 | unsigned long flag; |
155 | struct dp_event *todo; | |
156 | u32 gndx; | |
157 | ||
158 | spin_lock_irqsave(&dp_priv->event_lock, flag); | |
159 | if (dp_priv->event_pndx == dp_priv->event_gndx) { | |
160 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
161 | return -ENOENT; | |
162 | } | |
220b856a | 163 | |
8ede2ecc KH |
164 | gndx = dp_priv->event_gndx; |
165 | while (dp_priv->event_pndx != gndx) { | |
166 | todo = &dp_priv->event_list[gndx]; | |
167 | if (todo->event_id == event) { | |
168 | todo->event_id = EV_NO_EVENT; /* deleted */ | |
169 | todo->delay = 0; | |
170 | } | |
171 | gndx++; | |
172 | gndx %= DP_EVENT_Q_MAX; | |
220b856a | 173 | } |
8ede2ecc | 174 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); |
220b856a | 175 | |
8ede2ecc | 176 | return 0; |
c943b494 CU |
177 | } |
178 | ||
f2f46b87 KH |
179 | void dp_display_signal_audio_start(struct msm_dp *dp_display) |
180 | { | |
181 | struct dp_display_private *dp; | |
182 | ||
183 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
184 | ||
185 | reinit_completion(&dp->audio_comp); | |
186 | } | |
187 | ||
158b9aa7 AK |
188 | void dp_display_signal_audio_complete(struct msm_dp *dp_display) |
189 | { | |
190 | struct dp_display_private *dp; | |
191 | ||
192 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
193 | ||
194 | complete_all(&dp->audio_comp); | |
195 | } | |
196 | ||
c943b494 CU |
197 | static int dp_display_bind(struct device *dev, struct device *master, |
198 | void *data) | |
199 | { | |
200 | int rc = 0; | |
201 | struct dp_display_private *dp; | |
202 | struct drm_device *drm; | |
203 | struct msm_drm_private *priv; | |
c943b494 CU |
204 | |
205 | drm = dev_get_drvdata(master); | |
206 | ||
061eb621 AK |
207 | dp = container_of(g_dp_display, |
208 | struct dp_display_private, dp_display); | |
c943b494 CU |
209 | |
210 | dp->dp_display.drm_dev = drm; | |
211 | priv = drm->dev_private; | |
212 | priv->dp = &(dp->dp_display); | |
213 | ||
214 | rc = dp->parser->parse(dp->parser); | |
215 | if (rc) { | |
216 | DRM_ERROR("device tree parsing failed\n"); | |
217 | goto end; | |
218 | } | |
219 | ||
220 | rc = dp_aux_register(dp->aux); | |
221 | if (rc) { | |
222 | DRM_ERROR("DRM DP AUX register failed\n"); | |
223 | goto end; | |
224 | } | |
225 | ||
226 | rc = dp_power_client_init(dp->power); | |
227 | if (rc) { | |
228 | DRM_ERROR("Power client create failed\n"); | |
229 | goto end; | |
230 | } | |
d13e36d7 AK |
231 | |
232 | rc = dp_register_audio_driver(dev, dp->audio); | |
f913454a | 233 | if (rc) |
d13e36d7 | 234 | DRM_ERROR("Audio registration Dp failed\n"); |
d13e36d7 | 235 | |
c943b494 CU |
236 | end: |
237 | return rc; | |
238 | } | |
239 | ||
240 | static void dp_display_unbind(struct device *dev, struct device *master, | |
241 | void *data) | |
242 | { | |
243 | struct dp_display_private *dp; | |
c943b494 CU |
244 | struct drm_device *drm = dev_get_drvdata(master); |
245 | struct msm_drm_private *priv = drm->dev_private; | |
246 | ||
061eb621 AK |
247 | dp = container_of(g_dp_display, |
248 | struct dp_display_private, dp_display); | |
c943b494 CU |
249 | |
250 | dp_power_client_deinit(dp->power); | |
251 | dp_aux_unregister(dp->aux); | |
252 | priv->dp = NULL; | |
253 | } | |
254 | ||
255 | static const struct component_ops dp_display_comp_ops = { | |
256 | .bind = dp_display_bind, | |
257 | .unbind = dp_display_unbind, | |
258 | }; | |
259 | ||
260 | static bool dp_display_is_ds_bridge(struct dp_panel *panel) | |
261 | { | |
262 | return (panel->dpcd[DP_DOWNSTREAMPORT_PRESENT] & | |
263 | DP_DWN_STRM_PORT_PRESENT); | |
264 | } | |
265 | ||
266 | static bool dp_display_is_sink_count_zero(struct dp_display_private *dp) | |
267 | { | |
601f0479 MR |
268 | DRM_DEBUG_DP("present=%#x sink_count=%d\n", dp->panel->dpcd[DP_DOWNSTREAMPORT_PRESENT], |
269 | dp->link->sink_count); | |
c943b494 CU |
270 | return dp_display_is_ds_bridge(dp->panel) && |
271 | (dp->link->sink_count == 0); | |
272 | } | |
273 | ||
274 | static void dp_display_send_hpd_event(struct msm_dp *dp_display) | |
275 | { | |
276 | struct dp_display_private *dp; | |
277 | struct drm_connector *connector; | |
278 | ||
279 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
280 | ||
281 | connector = dp->dp_display.connector; | |
282 | drm_helper_hpd_irq_event(connector->dev); | |
283 | } | |
284 | ||
c58eb1b5 | 285 | |
c58eb1b5 KH |
286 | static int dp_display_send_hpd_notification(struct dp_display_private *dp, |
287 | bool hpd) | |
288 | { | |
c943b494 CU |
289 | if ((hpd && dp->dp_display.is_connected) || |
290 | (!hpd && !dp->dp_display.is_connected)) { | |
291 | DRM_DEBUG_DP("HPD already %s\n", (hpd ? "on" : "off")); | |
c943b494 CU |
292 | return 0; |
293 | } | |
294 | ||
295 | /* reset video pattern flag on disconnect */ | |
296 | if (!hpd) | |
297 | dp->panel->video_test = false; | |
298 | ||
299 | dp->dp_display.is_connected = hpd; | |
c943b494 | 300 | |
601f0479 | 301 | DRM_DEBUG_DP("hpd=%d\n", hpd); |
c943b494 CU |
302 | dp_display_send_hpd_event(&dp->dp_display); |
303 | ||
c943b494 CU |
304 | return 0; |
305 | } | |
306 | ||
307 | static int dp_display_process_hpd_high(struct dp_display_private *dp) | |
308 | { | |
309 | int rc = 0; | |
310 | struct edid *edid; | |
311 | ||
c943b494 CU |
312 | dp->panel->max_dp_lanes = dp->parser->max_dp_lanes; |
313 | ||
314 | rc = dp_panel_read_sink_caps(dp->panel, dp->dp_display.connector); | |
315 | if (rc) | |
8ede2ecc | 316 | goto end; |
c943b494 CU |
317 | |
318 | dp_link_process_request(dp->link); | |
319 | ||
c943b494 CU |
320 | edid = dp->panel->edid; |
321 | ||
322 | dp->audio_supported = drm_detect_monitor_audio(edid); | |
c943b494 CU |
323 | dp_panel_handle_sink_request(dp->panel); |
324 | ||
325 | dp->dp_display.max_pclk_khz = DP_MAX_PIXEL_CLK_KHZ; | |
326 | dp->dp_display.max_dp_lanes = dp->parser->max_dp_lanes; | |
8ede2ecc | 327 | |
f21c8a27 KH |
328 | /* |
329 | * set sink to normal operation mode -- D0 | |
330 | * before dpcd read | |
331 | */ | |
332 | dp_link_psm_config(dp->link, &dp->panel->link_info, false); | |
333 | ||
6625e263 | 334 | dp_link_reset_phy_params_vx_px(dp->link); |
8ede2ecc KH |
335 | rc = dp_ctrl_on_link(dp->ctrl); |
336 | if (rc) { | |
337 | DRM_ERROR("failed to complete DP link training\n"); | |
338 | goto end; | |
339 | } | |
340 | ||
341 | dp_add_event(dp, EV_USER_NOTIFICATION, true, 0); | |
342 | ||
c943b494 CU |
343 | end: |
344 | return rc; | |
345 | } | |
346 | ||
ea9f337c | 347 | static void dp_display_host_init(struct dp_display_private *dp, int reset) |
c943b494 CU |
348 | { |
349 | bool flip = false; | |
350 | ||
601f0479 | 351 | DRM_DEBUG_DP("core_initialized=%d\n", dp->core_initialized); |
c943b494 CU |
352 | if (dp->core_initialized) { |
353 | DRM_DEBUG_DP("DP core already initialized\n"); | |
354 | return; | |
355 | } | |
356 | ||
357 | if (dp->usbpd->orientation == ORIENTATION_CC2) | |
358 | flip = true; | |
359 | ||
360 | dp_power_init(dp->power, flip); | |
ea9f337c | 361 | dp_ctrl_host_init(dp->ctrl, flip, reset); |
c943b494 CU |
362 | dp_aux_init(dp->aux); |
363 | dp->core_initialized = true; | |
364 | } | |
365 | ||
19e52bcb KH |
366 | static void dp_display_host_deinit(struct dp_display_private *dp) |
367 | { | |
368 | if (!dp->core_initialized) { | |
369 | DRM_DEBUG_DP("DP core not initialized\n"); | |
370 | return; | |
371 | } | |
372 | ||
373 | dp_ctrl_host_deinit(dp->ctrl); | |
374 | dp_aux_deinit(dp->aux); | |
375 | dp_power_deinit(dp->power); | |
376 | ||
377 | dp->core_initialized = false; | |
378 | } | |
379 | ||
c943b494 CU |
380 | static int dp_display_usbpd_configure_cb(struct device *dev) |
381 | { | |
382 | int rc = 0; | |
383 | struct dp_display_private *dp; | |
384 | ||
385 | if (!dev) { | |
386 | DRM_ERROR("invalid dev\n"); | |
387 | rc = -EINVAL; | |
388 | goto end; | |
389 | } | |
390 | ||
061eb621 AK |
391 | dp = container_of(g_dp_display, |
392 | struct dp_display_private, dp_display); | |
c943b494 | 393 | |
ea9f337c | 394 | dp_display_host_init(dp, false); |
c943b494 | 395 | |
8ede2ecc | 396 | rc = dp_display_process_hpd_high(dp); |
c943b494 CU |
397 | end: |
398 | return rc; | |
399 | } | |
400 | ||
c943b494 CU |
401 | static int dp_display_usbpd_disconnect_cb(struct device *dev) |
402 | { | |
403 | int rc = 0; | |
404 | struct dp_display_private *dp; | |
405 | ||
d13e36d7 AK |
406 | if (!dev) { |
407 | DRM_ERROR("invalid dev\n"); | |
408 | rc = -EINVAL; | |
409 | return rc; | |
410 | } | |
411 | ||
061eb621 AK |
412 | dp = container_of(g_dp_display, |
413 | struct dp_display_private, dp_display); | |
c943b494 | 414 | |
8ede2ecc | 415 | dp_add_event(dp, EV_USER_NOTIFICATION, false, 0); |
c943b494 | 416 | |
c943b494 CU |
417 | return rc; |
418 | } | |
419 | ||
420 | static void dp_display_handle_video_request(struct dp_display_private *dp) | |
421 | { | |
422 | if (dp->link->sink_request & DP_TEST_LINK_VIDEO_PATTERN) { | |
c943b494 | 423 | dp->panel->video_test = true; |
c943b494 CU |
424 | dp_link_send_test_response(dp->link); |
425 | } | |
426 | } | |
427 | ||
c58eb1b5 | 428 | static int dp_display_handle_port_ststus_changed(struct dp_display_private *dp) |
c943b494 | 429 | { |
c58eb1b5 | 430 | int rc = 0; |
c943b494 | 431 | |
c58eb1b5 KH |
432 | if (dp_display_is_sink_count_zero(dp)) { |
433 | DRM_DEBUG_DP("sink count is zero, nothing to do\n"); | |
434 | if (dp->hpd_state != ST_DISCONNECTED) { | |
435 | dp->hpd_state = ST_DISCONNECT_PENDING; | |
436 | dp_add_event(dp, EV_USER_NOTIFICATION, false, 0); | |
437 | } | |
438 | } else { | |
439 | if (dp->hpd_state == ST_DISCONNECTED) { | |
440 | dp->hpd_state = ST_CONNECT_PENDING; | |
441 | rc = dp_display_process_hpd_high(dp); | |
442 | if (rc) | |
443 | dp->hpd_state = ST_DISCONNECTED; | |
c943b494 | 444 | } |
c58eb1b5 KH |
445 | } |
446 | ||
447 | return rc; | |
448 | } | |
449 | ||
450 | static int dp_display_handle_irq_hpd(struct dp_display_private *dp) | |
451 | { | |
452 | u32 sink_request = dp->link->sink_request; | |
c943b494 | 453 | |
601f0479 | 454 | DRM_DEBUG_DP("%d\n", sink_request); |
c58eb1b5 KH |
455 | if (dp->hpd_state == ST_DISCONNECTED) { |
456 | if (sink_request & DP_LINK_STATUS_UPDATED) { | |
601f0479 | 457 | DRM_DEBUG_DP("Disconnected sink_request: %d\n", sink_request); |
c58eb1b5 KH |
458 | DRM_ERROR("Disconnected, no DP_LINK_STATUS_UPDATED\n"); |
459 | return -EINVAL; | |
460 | } | |
c943b494 CU |
461 | } |
462 | ||
463 | dp_ctrl_handle_sink_request(dp->ctrl); | |
464 | ||
c58eb1b5 | 465 | if (sink_request & DP_TEST_LINK_VIDEO_PATTERN) |
8ede2ecc | 466 | dp_display_handle_video_request(dp); |
c943b494 CU |
467 | |
468 | return 0; | |
469 | } | |
470 | ||
471 | static int dp_display_usbpd_attention_cb(struct device *dev) | |
472 | { | |
473 | int rc = 0; | |
26b8d66a | 474 | u32 sink_request; |
c943b494 CU |
475 | struct dp_display_private *dp; |
476 | ||
477 | if (!dev) { | |
478 | DRM_ERROR("invalid dev\n"); | |
479 | return -EINVAL; | |
480 | } | |
481 | ||
061eb621 AK |
482 | dp = container_of(g_dp_display, |
483 | struct dp_display_private, dp_display); | |
c943b494 | 484 | |
8ede2ecc KH |
485 | /* check for any test request issued by sink */ |
486 | rc = dp_link_process_request(dp->link); | |
26b8d66a KH |
487 | if (!rc) { |
488 | sink_request = dp->link->sink_request; | |
601f0479 | 489 | DRM_DEBUG_DP("hpd_state=%d sink_request=%d\n", dp->hpd_state, sink_request); |
c58eb1b5 KH |
490 | if (sink_request & DS_PORT_STATUS_CHANGED) |
491 | rc = dp_display_handle_port_ststus_changed(dp); | |
492 | else | |
493 | rc = dp_display_handle_irq_hpd(dp); | |
26b8d66a | 494 | } |
c943b494 | 495 | |
8ede2ecc KH |
496 | return rc; |
497 | } | |
c943b494 | 498 | |
8ede2ecc KH |
499 | static int dp_hpd_plug_handle(struct dp_display_private *dp, u32 data) |
500 | { | |
501 | struct dp_usbpd *hpd = dp->usbpd; | |
502 | u32 state; | |
503 | u32 tout = DP_TIMEOUT_5_SECOND; | |
504 | int ret; | |
505 | ||
506 | if (!hpd) | |
507 | return 0; | |
508 | ||
509 | mutex_lock(&dp->event_mutex); | |
510 | ||
19e52bcb | 511 | state = dp->hpd_state; |
601f0479 | 512 | DRM_DEBUG_DP("hpd_state=%d\n", state); |
62671d2e | 513 | if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) { |
8ede2ecc KH |
514 | mutex_unlock(&dp->event_mutex); |
515 | return 0; | |
c943b494 CU |
516 | } |
517 | ||
8ede2ecc KH |
518 | if (state == ST_CONNECT_PENDING || state == ST_CONNECTED) { |
519 | mutex_unlock(&dp->event_mutex); | |
520 | return 0; | |
c943b494 CU |
521 | } |
522 | ||
8ede2ecc KH |
523 | if (state == ST_DISCONNECT_PENDING) { |
524 | /* wait until ST_DISCONNECTED */ | |
525 | dp_add_event(dp, EV_HPD_PLUG_INT, 0, 1); /* delay = 1 */ | |
526 | mutex_unlock(&dp->event_mutex); | |
527 | return 0; | |
528 | } | |
529 | ||
19e52bcb | 530 | dp->hpd_state = ST_CONNECT_PENDING; |
8ede2ecc KH |
531 | |
532 | hpd->hpd_high = 1; | |
533 | ||
534 | ret = dp_display_usbpd_configure_cb(&dp->pdev->dev); | |
62671d2e | 535 | if (ret) { /* link train failed */ |
8ede2ecc | 536 | hpd->hpd_high = 0; |
19e52bcb | 537 | dp->hpd_state = ST_DISCONNECTED; |
231a04fc KH |
538 | |
539 | if (ret == -ECONNRESET) { /* cable unplugged */ | |
540 | dp->core_initialized = false; | |
541 | } | |
542 | ||
62671d2e KH |
543 | } else { |
544 | /* start sentinel checking in case of missing uevent */ | |
545 | dp_add_event(dp, EV_CONNECT_PENDING_TIMEOUT, 0, tout); | |
8ede2ecc KH |
546 | } |
547 | ||
f21c8a27 KH |
548 | /* enable HDP irq_hpd/replug interrupt */ |
549 | dp_catalog_hpd_config_intr(dp->catalog, | |
550 | DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, true); | |
551 | ||
8ede2ecc KH |
552 | mutex_unlock(&dp->event_mutex); |
553 | ||
554 | /* uevent will complete connection part */ | |
555 | return 0; | |
556 | }; | |
557 | ||
558 | static int dp_display_enable(struct dp_display_private *dp, u32 data); | |
559 | static int dp_display_disable(struct dp_display_private *dp, u32 data); | |
560 | ||
561 | static int dp_connect_pending_timeout(struct dp_display_private *dp, u32 data) | |
562 | { | |
563 | u32 state; | |
564 | ||
565 | mutex_lock(&dp->event_mutex); | |
566 | ||
19e52bcb | 567 | state = dp->hpd_state; |
d9aa6571 | 568 | if (state == ST_CONNECT_PENDING) |
19e52bcb | 569 | dp->hpd_state = ST_CONNECTED; |
8ede2ecc KH |
570 | |
571 | mutex_unlock(&dp->event_mutex); | |
572 | ||
573 | return 0; | |
574 | } | |
575 | ||
bf4a1b31 AK |
576 | static void dp_display_handle_plugged_change(struct msm_dp *dp_display, |
577 | bool plugged) | |
578 | { | |
e8c76581 AK |
579 | struct dp_display_private *dp; |
580 | ||
581 | dp = container_of(dp_display, | |
582 | struct dp_display_private, dp_display); | |
583 | ||
584 | /* notify audio subsystem only if sink supports audio */ | |
585 | if (dp_display->plugged_cb && dp_display->codec_dev && | |
586 | dp->audio_supported) | |
bf4a1b31 AK |
587 | dp_display->plugged_cb(dp_display->codec_dev, plugged); |
588 | } | |
589 | ||
8ede2ecc KH |
590 | static int dp_hpd_unplug_handle(struct dp_display_private *dp, u32 data) |
591 | { | |
592 | struct dp_usbpd *hpd = dp->usbpd; | |
593 | u32 state; | |
594 | ||
595 | if (!hpd) | |
596 | return 0; | |
597 | ||
598 | mutex_lock(&dp->event_mutex); | |
599 | ||
19e52bcb | 600 | state = dp->hpd_state; |
f21c8a27 KH |
601 | |
602 | /* disable irq_hpd/replug interrupts */ | |
603 | dp_catalog_hpd_config_intr(dp->catalog, | |
604 | DP_DP_IRQ_HPD_INT_MASK | DP_DP_HPD_REPLUG_INT_MASK, false); | |
605 | ||
606 | /* unplugged, no more irq_hpd handle */ | |
607 | dp_del_event(dp, EV_IRQ_HPD_INT); | |
608 | ||
609 | if (state == ST_DISCONNECTED) { | |
610 | /* triggered by irq_hdp with sink_count = 0 */ | |
611 | if (dp->link->sink_count == 0) { | |
612 | dp_ctrl_off_phy(dp->ctrl); | |
613 | hpd->hpd_high = 0; | |
614 | dp->core_initialized = false; | |
615 | } | |
616 | mutex_unlock(&dp->event_mutex); | |
617 | return 0; | |
618 | } | |
619 | ||
620 | if (state == ST_DISCONNECT_PENDING) { | |
8ede2ecc KH |
621 | mutex_unlock(&dp->event_mutex); |
622 | return 0; | |
623 | } | |
624 | ||
625 | if (state == ST_CONNECT_PENDING) { | |
626 | /* wait until CONNECTED */ | |
627 | dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 1); /* delay = 1 */ | |
628 | mutex_unlock(&dp->event_mutex); | |
629 | return 0; | |
630 | } | |
631 | ||
19e52bcb | 632 | dp->hpd_state = ST_DISCONNECT_PENDING; |
8ede2ecc | 633 | |
f21c8a27 KH |
634 | /* disable HPD plug interrupts */ |
635 | dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, false); | |
8ede2ecc KH |
636 | |
637 | hpd->hpd_high = 0; | |
638 | ||
639 | /* | |
640 | * We don't need separate work for disconnect as | |
641 | * connect/attention interrupts are disabled | |
642 | */ | |
643 | dp_display_usbpd_disconnect_cb(&dp->pdev->dev); | |
644 | ||
62671d2e | 645 | /* start sentinel checking in case of missing uevent */ |
8ede2ecc KH |
646 | dp_add_event(dp, EV_DISCONNECT_PENDING_TIMEOUT, 0, DP_TIMEOUT_5_SECOND); |
647 | ||
601f0479 | 648 | DRM_DEBUG_DP("hpd_state=%d\n", state); |
bf4a1b31 | 649 | /* signal the disconnect event early to ensure proper teardown */ |
c703d578 | 650 | dp_display_handle_plugged_change(g_dp_display, false); |
bf4a1b31 | 651 | |
f21c8a27 KH |
652 | /* enable HDP plug interrupt to prepare for next plugin */ |
653 | dp_catalog_hpd_config_intr(dp->catalog, DP_DP_HPD_PLUG_INT_MASK, true); | |
8ede2ecc KH |
654 | |
655 | /* uevent will complete disconnection part */ | |
656 | mutex_unlock(&dp->event_mutex); | |
657 | return 0; | |
658 | } | |
659 | ||
660 | static int dp_disconnect_pending_timeout(struct dp_display_private *dp, u32 data) | |
661 | { | |
662 | u32 state; | |
663 | ||
664 | mutex_lock(&dp->event_mutex); | |
665 | ||
19e52bcb | 666 | state = dp->hpd_state; |
d9aa6571 | 667 | if (state == ST_DISCONNECT_PENDING) |
19e52bcb | 668 | dp->hpd_state = ST_DISCONNECTED; |
8ede2ecc KH |
669 | |
670 | mutex_unlock(&dp->event_mutex); | |
671 | ||
672 | return 0; | |
673 | } | |
674 | ||
675 | static int dp_irq_hpd_handle(struct dp_display_private *dp, u32 data) | |
676 | { | |
677 | u32 state; | |
c58eb1b5 | 678 | int ret; |
8ede2ecc KH |
679 | |
680 | mutex_lock(&dp->event_mutex); | |
681 | ||
682 | /* irq_hpd can happen at either connected or disconnected state */ | |
19e52bcb | 683 | state = dp->hpd_state; |
f21c8a27 | 684 | if (state == ST_DISPLAY_OFF || state == ST_SUSPENDED) { |
8ede2ecc KH |
685 | mutex_unlock(&dp->event_mutex); |
686 | return 0; | |
687 | } | |
688 | ||
2b5f09ca KH |
689 | if (state == ST_CONNECT_PENDING) { |
690 | /* wait until ST_CONNECTED */ | |
691 | dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */ | |
692 | mutex_unlock(&dp->event_mutex); | |
693 | return 0; | |
694 | } | |
695 | ||
ea9f337c | 696 | if (state == ST_CONNECT_PENDING || state == ST_DISCONNECT_PENDING) { |
9fc41843 KH |
697 | /* wait until ST_CONNECTED */ |
698 | dp_add_event(dp, EV_IRQ_HPD_INT, 0, 1); /* delay = 1 */ | |
699 | mutex_unlock(&dp->event_mutex); | |
700 | return 0; | |
701 | } | |
702 | ||
c58eb1b5 KH |
703 | ret = dp_display_usbpd_attention_cb(&dp->pdev->dev); |
704 | if (ret == -ECONNRESET) { /* cable unplugged */ | |
705 | dp->core_initialized = false; | |
706 | } | |
601f0479 | 707 | DRM_DEBUG_DP("hpd_state=%d\n", state); |
8ede2ecc KH |
708 | |
709 | mutex_unlock(&dp->event_mutex); | |
710 | ||
711 | return 0; | |
c943b494 CU |
712 | } |
713 | ||
714 | static void dp_display_deinit_sub_modules(struct dp_display_private *dp) | |
715 | { | |
d11a9369 | 716 | dp_debug_put(dp->debug); |
c943b494 CU |
717 | dp_panel_put(dp->panel); |
718 | dp_aux_put(dp->aux); | |
d13e36d7 | 719 | dp_audio_put(dp->audio); |
c943b494 CU |
720 | } |
721 | ||
722 | static int dp_init_sub_modules(struct dp_display_private *dp) | |
723 | { | |
724 | int rc = 0; | |
725 | struct device *dev = &dp->pdev->dev; | |
726 | struct dp_usbpd_cb *cb = &dp->usbpd_cb; | |
727 | struct dp_panel_in panel_in = { | |
728 | .dev = dev, | |
729 | }; | |
730 | ||
731 | /* Callback APIs used for cable status change event */ | |
732 | cb->configure = dp_display_usbpd_configure_cb; | |
733 | cb->disconnect = dp_display_usbpd_disconnect_cb; | |
734 | cb->attention = dp_display_usbpd_attention_cb; | |
735 | ||
736 | dp->usbpd = dp_hpd_get(dev, cb); | |
737 | if (IS_ERR(dp->usbpd)) { | |
738 | rc = PTR_ERR(dp->usbpd); | |
739 | DRM_ERROR("failed to initialize hpd, rc = %d\n", rc); | |
740 | dp->usbpd = NULL; | |
741 | goto error; | |
742 | } | |
743 | ||
744 | dp->parser = dp_parser_get(dp->pdev); | |
745 | if (IS_ERR(dp->parser)) { | |
746 | rc = PTR_ERR(dp->parser); | |
747 | DRM_ERROR("failed to initialize parser, rc = %d\n", rc); | |
748 | dp->parser = NULL; | |
749 | goto error; | |
750 | } | |
751 | ||
752 | dp->catalog = dp_catalog_get(dev, &dp->parser->io); | |
753 | if (IS_ERR(dp->catalog)) { | |
754 | rc = PTR_ERR(dp->catalog); | |
755 | DRM_ERROR("failed to initialize catalog, rc = %d\n", rc); | |
756 | dp->catalog = NULL; | |
757 | goto error; | |
758 | } | |
759 | ||
ab387647 | 760 | dp->power = dp_power_get(dev, dp->parser); |
c943b494 CU |
761 | if (IS_ERR(dp->power)) { |
762 | rc = PTR_ERR(dp->power); | |
763 | DRM_ERROR("failed to initialize power, rc = %d\n", rc); | |
764 | dp->power = NULL; | |
765 | goto error; | |
766 | } | |
767 | ||
768 | dp->aux = dp_aux_get(dev, dp->catalog); | |
769 | if (IS_ERR(dp->aux)) { | |
770 | rc = PTR_ERR(dp->aux); | |
771 | DRM_ERROR("failed to initialize aux, rc = %d\n", rc); | |
772 | dp->aux = NULL; | |
773 | goto error; | |
774 | } | |
775 | ||
776 | dp->link = dp_link_get(dev, dp->aux); | |
777 | if (IS_ERR(dp->link)) { | |
778 | rc = PTR_ERR(dp->link); | |
779 | DRM_ERROR("failed to initialize link, rc = %d\n", rc); | |
780 | dp->link = NULL; | |
781 | goto error_link; | |
782 | } | |
783 | ||
784 | panel_in.aux = dp->aux; | |
785 | panel_in.catalog = dp->catalog; | |
786 | panel_in.link = dp->link; | |
787 | ||
788 | dp->panel = dp_panel_get(&panel_in); | |
789 | if (IS_ERR(dp->panel)) { | |
790 | rc = PTR_ERR(dp->panel); | |
791 | DRM_ERROR("failed to initialize panel, rc = %d\n", rc); | |
792 | dp->panel = NULL; | |
793 | goto error_link; | |
794 | } | |
795 | ||
796 | dp->ctrl = dp_ctrl_get(dev, dp->link, dp->panel, dp->aux, | |
797 | dp->power, dp->catalog, dp->parser); | |
798 | if (IS_ERR(dp->ctrl)) { | |
799 | rc = PTR_ERR(dp->ctrl); | |
800 | DRM_ERROR("failed to initialize ctrl, rc = %d\n", rc); | |
801 | dp->ctrl = NULL; | |
802 | goto error_ctrl; | |
803 | } | |
804 | ||
d13e36d7 AK |
805 | dp->audio = dp_audio_get(dp->pdev, dp->panel, dp->catalog); |
806 | if (IS_ERR(dp->audio)) { | |
807 | rc = PTR_ERR(dp->audio); | |
808 | pr_err("failed to initialize audio, rc = %d\n", rc); | |
809 | dp->audio = NULL; | |
11120e93 | 810 | goto error_ctrl; |
d13e36d7 AK |
811 | } |
812 | ||
c943b494 | 813 | return rc; |
d13e36d7 | 814 | |
c943b494 CU |
815 | error_ctrl: |
816 | dp_panel_put(dp->panel); | |
817 | error_link: | |
818 | dp_aux_put(dp->aux); | |
819 | error: | |
820 | return rc; | |
821 | } | |
822 | ||
823 | static int dp_display_set_mode(struct msm_dp *dp_display, | |
824 | struct dp_display_mode *mode) | |
825 | { | |
826 | struct dp_display_private *dp; | |
827 | ||
828 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
829 | ||
830 | dp->panel->dp_mode.drm_mode = mode->drm_mode; | |
831 | dp->panel->dp_mode.bpp = mode->bpp; | |
832 | dp->panel->dp_mode.capabilities = mode->capabilities; | |
833 | dp_panel_init_panel_info(dp->panel); | |
834 | return 0; | |
835 | } | |
836 | ||
837 | static int dp_display_prepare(struct msm_dp *dp) | |
838 | { | |
839 | return 0; | |
840 | } | |
841 | ||
8ede2ecc | 842 | static int dp_display_enable(struct dp_display_private *dp, u32 data) |
c943b494 CU |
843 | { |
844 | int rc = 0; | |
158b9aa7 | 845 | struct msm_dp *dp_display; |
c943b494 | 846 | |
158b9aa7 AK |
847 | dp_display = g_dp_display; |
848 | ||
601f0479 | 849 | DRM_DEBUG_DP("sink_count=%d\n", dp->link->sink_count); |
231a04fc KH |
850 | if (dp_display->power_on) { |
851 | DRM_DEBUG_DP("Link already setup, return\n"); | |
852 | return 0; | |
853 | } | |
854 | ||
8ede2ecc | 855 | rc = dp_ctrl_on_stream(dp->ctrl); |
c943b494 | 856 | if (!rc) |
158b9aa7 | 857 | dp_display->power_on = true; |
c943b494 | 858 | |
c943b494 CU |
859 | return rc; |
860 | } | |
861 | ||
862 | static int dp_display_post_enable(struct msm_dp *dp_display) | |
863 | { | |
d13e36d7 AK |
864 | struct dp_display_private *dp; |
865 | u32 rate; | |
866 | ||
867 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
868 | ||
869 | rate = dp->link->link_params.rate; | |
870 | ||
871 | if (dp->audio_supported) { | |
872 | dp->audio->bw_code = drm_dp_link_rate_to_bw_code(rate); | |
873 | dp->audio->lane_count = dp->link->link_params.num_lanes; | |
874 | } | |
875 | ||
a1f5bda9 AK |
876 | /* signal the connect event late to synchronize video and display */ |
877 | dp_display_handle_plugged_change(dp_display, true); | |
c943b494 CU |
878 | return 0; |
879 | } | |
880 | ||
8ede2ecc | 881 | static int dp_display_disable(struct dp_display_private *dp, u32 data) |
c943b494 | 882 | { |
158b9aa7 AK |
883 | struct msm_dp *dp_display; |
884 | ||
885 | dp_display = g_dp_display; | |
886 | ||
231a04fc KH |
887 | if (!dp_display->power_on) |
888 | return 0; | |
889 | ||
158b9aa7 AK |
890 | /* wait only if audio was enabled */ |
891 | if (dp_display->audio_enabled) { | |
c703d578 | 892 | /* signal the disconnect event */ |
c703d578 | 893 | dp_display_handle_plugged_change(dp_display, false); |
158b9aa7 AK |
894 | if (!wait_for_completion_timeout(&dp->audio_comp, |
895 | HZ * 5)) | |
896 | DRM_ERROR("audio comp timeout\n"); | |
897 | } | |
898 | ||
899 | dp_display->audio_enabled = false; | |
900 | ||
f21c8a27 KH |
901 | /* triggered by irq_hpd with sink_count = 0 */ |
902 | if (dp->link->sink_count == 0) { | |
903 | dp_ctrl_off_link_stream(dp->ctrl); | |
904 | } else { | |
905 | dp_ctrl_off(dp->ctrl); | |
906 | dp->core_initialized = false; | |
907 | } | |
c943b494 | 908 | |
158b9aa7 | 909 | dp_display->power_on = false; |
c943b494 | 910 | |
601f0479 | 911 | DRM_DEBUG_DP("sink count: %d\n", dp->link->sink_count); |
c943b494 CU |
912 | return 0; |
913 | } | |
914 | ||
915 | static int dp_display_unprepare(struct msm_dp *dp) | |
916 | { | |
917 | return 0; | |
918 | } | |
919 | ||
a1f5bda9 AK |
920 | int dp_display_set_plugged_cb(struct msm_dp *dp_display, |
921 | hdmi_codec_plugged_cb fn, struct device *codec_dev) | |
922 | { | |
923 | bool plugged; | |
924 | ||
925 | dp_display->plugged_cb = fn; | |
926 | dp_display->codec_dev = codec_dev; | |
927 | plugged = dp_display->is_connected; | |
928 | dp_display_handle_plugged_change(dp_display, plugged); | |
929 | ||
930 | return 0; | |
931 | } | |
932 | ||
c943b494 CU |
933 | int dp_display_validate_mode(struct msm_dp *dp, u32 mode_pclk_khz) |
934 | { | |
935 | const u32 num_components = 3, default_bpp = 24; | |
936 | struct dp_display_private *dp_display; | |
937 | struct dp_link_info *link_info; | |
938 | u32 mode_rate_khz = 0, supported_rate_khz = 0, mode_bpp = 0; | |
939 | ||
940 | if (!dp || !mode_pclk_khz || !dp->connector) { | |
941 | DRM_ERROR("invalid params\n"); | |
942 | return -EINVAL; | |
943 | } | |
944 | ||
945 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
946 | link_info = &dp_display->panel->link_info; | |
947 | ||
948 | mode_bpp = dp->connector->display_info.bpc * num_components; | |
949 | if (!mode_bpp) | |
950 | mode_bpp = default_bpp; | |
951 | ||
952 | mode_bpp = dp_panel_get_mode_bpp(dp_display->panel, | |
953 | mode_bpp, mode_pclk_khz); | |
954 | ||
955 | mode_rate_khz = mode_pclk_khz * mode_bpp; | |
956 | supported_rate_khz = link_info->num_lanes * link_info->rate * 8; | |
957 | ||
958 | if (mode_rate_khz > supported_rate_khz) | |
959 | return MODE_BAD; | |
960 | ||
961 | return MODE_OK; | |
962 | } | |
963 | ||
964 | int dp_display_get_modes(struct msm_dp *dp, | |
965 | struct dp_display_mode *dp_mode) | |
966 | { | |
967 | struct dp_display_private *dp_display; | |
968 | int ret = 0; | |
969 | ||
970 | if (!dp) { | |
971 | DRM_ERROR("invalid params\n"); | |
972 | return 0; | |
973 | } | |
974 | ||
975 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
976 | ||
977 | ret = dp_panel_get_modes(dp_display->panel, | |
978 | dp->connector, dp_mode); | |
979 | if (dp_mode->drm_mode.clock) | |
980 | dp->max_pclk_khz = dp_mode->drm_mode.clock; | |
981 | return ret; | |
982 | } | |
983 | ||
984 | bool dp_display_check_video_test(struct msm_dp *dp) | |
985 | { | |
986 | struct dp_display_private *dp_display; | |
987 | ||
988 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
989 | ||
990 | return dp_display->panel->video_test; | |
991 | } | |
992 | ||
993 | int dp_display_get_test_bpp(struct msm_dp *dp) | |
994 | { | |
995 | struct dp_display_private *dp_display; | |
996 | ||
997 | if (!dp) { | |
998 | DRM_ERROR("invalid params\n"); | |
999 | return 0; | |
1000 | } | |
1001 | ||
1002 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
1003 | ||
1004 | return dp_link_bit_depth_to_bpp( | |
1005 | dp_display->link->test_video.test_bit_depth); | |
1006 | } | |
1007 | ||
eb9d6c7e | 1008 | void msm_dp_snapshot(struct msm_disp_state *disp_state, struct msm_dp *dp) |
0f6090f3 AK |
1009 | { |
1010 | struct dp_display_private *dp_display; | |
0f6090f3 AK |
1011 | |
1012 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
0f6090f3 AK |
1013 | |
1014 | /* | |
1015 | * if we are reading registers we need the link clocks to be on | |
1016 | * however till DP cable is connected this will not happen as we | |
1017 | * do not know the resolution to power up with. Hence check the | |
1018 | * power_on status before dumping DP registers to avoid crash due | |
1019 | * to unclocked access | |
1020 | */ | |
1021 | mutex_lock(&dp_display->event_mutex); | |
1022 | ||
1023 | if (!dp->power_on) { | |
1024 | mutex_unlock(&dp_display->event_mutex); | |
1025 | return; | |
1026 | } | |
1027 | ||
1028 | dp_catalog_snapshot(dp_display->catalog, disp_state); | |
1029 | ||
1030 | mutex_unlock(&dp_display->event_mutex); | |
1031 | } | |
1032 | ||
8ede2ecc KH |
1033 | static void dp_display_config_hpd(struct dp_display_private *dp) |
1034 | { | |
1035 | ||
ea9f337c | 1036 | dp_display_host_init(dp, true); |
8ede2ecc KH |
1037 | dp_catalog_ctrl_hpd_config(dp->catalog); |
1038 | ||
1039 | /* Enable interrupt first time | |
1040 | * we are leaving dp clocks on during disconnect | |
1041 | * and never disable interrupt | |
1042 | */ | |
1043 | enable_irq(dp->irq); | |
1044 | } | |
1045 | ||
1046 | static int hpd_event_thread(void *data) | |
1047 | { | |
1048 | struct dp_display_private *dp_priv; | |
1049 | unsigned long flag; | |
1050 | struct dp_event *todo; | |
1051 | int timeout_mode = 0; | |
1052 | ||
1053 | dp_priv = (struct dp_display_private *)data; | |
1054 | ||
1055 | while (1) { | |
1056 | if (timeout_mode) { | |
1057 | wait_event_timeout(dp_priv->event_q, | |
1058 | (dp_priv->event_pndx == dp_priv->event_gndx), | |
1059 | EVENT_TIMEOUT); | |
1060 | } else { | |
710a040a SB |
1061 | wait_event_interruptible(dp_priv->event_q, |
1062 | (dp_priv->event_pndx != dp_priv->event_gndx)); | |
8ede2ecc KH |
1063 | } |
1064 | spin_lock_irqsave(&dp_priv->event_lock, flag); | |
1065 | todo = &dp_priv->event_list[dp_priv->event_gndx]; | |
1066 | if (todo->delay) { | |
1067 | struct dp_event *todo_next; | |
1068 | ||
1069 | dp_priv->event_gndx++; | |
1070 | dp_priv->event_gndx %= DP_EVENT_Q_MAX; | |
1071 | ||
1072 | /* re enter delay event into q */ | |
1073 | todo_next = &dp_priv->event_list[dp_priv->event_pndx++]; | |
1074 | dp_priv->event_pndx %= DP_EVENT_Q_MAX; | |
1075 | todo_next->event_id = todo->event_id; | |
1076 | todo_next->data = todo->data; | |
1077 | todo_next->delay = todo->delay - 1; | |
1078 | ||
1079 | /* clean up older event */ | |
1080 | todo->event_id = EV_NO_EVENT; | |
1081 | todo->delay = 0; | |
1082 | ||
1083 | /* switch to timeout mode */ | |
1084 | timeout_mode = 1; | |
1085 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
1086 | continue; | |
1087 | } | |
1088 | ||
1089 | /* timeout with no events in q */ | |
1090 | if (dp_priv->event_pndx == dp_priv->event_gndx) { | |
1091 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
1092 | continue; | |
1093 | } | |
1094 | ||
1095 | dp_priv->event_gndx++; | |
1096 | dp_priv->event_gndx %= DP_EVENT_Q_MAX; | |
1097 | timeout_mode = 0; | |
1098 | spin_unlock_irqrestore(&dp_priv->event_lock, flag); | |
1099 | ||
1100 | switch (todo->event_id) { | |
1101 | case EV_HPD_INIT_SETUP: | |
1102 | dp_display_config_hpd(dp_priv); | |
1103 | break; | |
1104 | case EV_HPD_PLUG_INT: | |
1105 | dp_hpd_plug_handle(dp_priv, todo->data); | |
1106 | break; | |
1107 | case EV_HPD_UNPLUG_INT: | |
1108 | dp_hpd_unplug_handle(dp_priv, todo->data); | |
1109 | break; | |
1110 | case EV_IRQ_HPD_INT: | |
1111 | dp_irq_hpd_handle(dp_priv, todo->data); | |
1112 | break; | |
1113 | case EV_HPD_REPLUG_INT: | |
1114 | /* do nothing */ | |
1115 | break; | |
1116 | case EV_USER_NOTIFICATION: | |
1117 | dp_display_send_hpd_notification(dp_priv, | |
1118 | todo->data); | |
1119 | break; | |
1120 | case EV_CONNECT_PENDING_TIMEOUT: | |
1121 | dp_connect_pending_timeout(dp_priv, | |
1122 | todo->data); | |
1123 | break; | |
1124 | case EV_DISCONNECT_PENDING_TIMEOUT: | |
1125 | dp_disconnect_pending_timeout(dp_priv, | |
1126 | todo->data); | |
1127 | break; | |
1128 | default: | |
1129 | break; | |
1130 | } | |
1131 | } | |
1132 | ||
1133 | return 0; | |
1134 | } | |
1135 | ||
1136 | static void dp_hpd_event_setup(struct dp_display_private *dp_priv) | |
1137 | { | |
1138 | init_waitqueue_head(&dp_priv->event_q); | |
1139 | spin_lock_init(&dp_priv->event_lock); | |
1140 | ||
1141 | kthread_run(hpd_event_thread, dp_priv, "dp_hpd_handler"); | |
1142 | } | |
1143 | ||
1144 | static irqreturn_t dp_display_irq_handler(int irq, void *dev_id) | |
1145 | { | |
1146 | struct dp_display_private *dp = dev_id; | |
1147 | irqreturn_t ret = IRQ_HANDLED; | |
1148 | u32 hpd_isr_status; | |
1149 | ||
1150 | if (!dp) { | |
1151 | DRM_ERROR("invalid data\n"); | |
1152 | return IRQ_NONE; | |
1153 | } | |
1154 | ||
1155 | hpd_isr_status = dp_catalog_hpd_get_intr_status(dp->catalog); | |
1156 | ||
601f0479 | 1157 | DRM_DEBUG_DP("hpd isr status=%#x\n", hpd_isr_status); |
8ede2ecc KH |
1158 | if (hpd_isr_status & 0x0F) { |
1159 | /* hpd related interrupts */ | |
1160 | if (hpd_isr_status & DP_DP_HPD_PLUG_INT_MASK || | |
1161 | hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) { | |
1162 | dp_add_event(dp, EV_HPD_PLUG_INT, 0, 0); | |
1163 | } | |
1164 | ||
1165 | if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) { | |
62671d2e | 1166 | /* stop sentinel connect pending checking */ |
8ede2ecc KH |
1167 | dp_del_event(dp, EV_CONNECT_PENDING_TIMEOUT); |
1168 | dp_add_event(dp, EV_IRQ_HPD_INT, 0, 0); | |
1169 | } | |
1170 | ||
1171 | if (hpd_isr_status & DP_DP_HPD_REPLUG_INT_MASK) | |
1172 | dp_add_event(dp, EV_HPD_REPLUG_INT, 0, 0); | |
1173 | ||
1174 | if (hpd_isr_status & DP_DP_HPD_UNPLUG_INT_MASK) | |
1175 | dp_add_event(dp, EV_HPD_UNPLUG_INT, 0, 0); | |
1176 | } | |
1177 | ||
1178 | /* DP controller isr */ | |
1179 | dp_ctrl_isr(dp->ctrl); | |
1180 | ||
1181 | /* DP aux isr */ | |
1182 | dp_aux_isr(dp->aux); | |
1183 | ||
1184 | return ret; | |
1185 | } | |
1186 | ||
1187 | int dp_display_request_irq(struct msm_dp *dp_display) | |
1188 | { | |
1189 | int rc = 0; | |
1190 | struct dp_display_private *dp; | |
1191 | ||
1192 | if (!dp_display) { | |
1193 | DRM_ERROR("invalid input\n"); | |
1194 | return -EINVAL; | |
1195 | } | |
1196 | ||
1197 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
1198 | ||
1199 | dp->irq = irq_of_parse_and_map(dp->pdev->dev.of_node, 0); | |
1200 | if (dp->irq < 0) { | |
1201 | rc = dp->irq; | |
1202 | DRM_ERROR("failed to get irq: %d\n", rc); | |
1203 | return rc; | |
1204 | } | |
1205 | ||
1206 | rc = devm_request_irq(&dp->pdev->dev, dp->irq, | |
1207 | dp_display_irq_handler, | |
1208 | IRQF_TRIGGER_HIGH, "dp_display_isr", dp); | |
1209 | if (rc < 0) { | |
1210 | DRM_ERROR("failed to request IRQ%u: %d\n", | |
1211 | dp->irq, rc); | |
1212 | return rc; | |
1213 | } | |
1214 | disable_irq(dp->irq); | |
1215 | ||
1216 | return 0; | |
1217 | } | |
1218 | ||
c943b494 CU |
1219 | static int dp_display_probe(struct platform_device *pdev) |
1220 | { | |
1221 | int rc = 0; | |
1222 | struct dp_display_private *dp; | |
1223 | ||
1224 | if (!pdev || !pdev->dev.of_node) { | |
1225 | DRM_ERROR("pdev not found\n"); | |
1226 | return -ENODEV; | |
1227 | } | |
1228 | ||
1229 | dp = devm_kzalloc(&pdev->dev, sizeof(*dp), GFP_KERNEL); | |
1230 | if (!dp) | |
1231 | return -ENOMEM; | |
1232 | ||
c943b494 CU |
1233 | dp->pdev = pdev; |
1234 | dp->name = "drm_dp"; | |
1235 | ||
1236 | rc = dp_init_sub_modules(dp); | |
1237 | if (rc) { | |
1238 | DRM_ERROR("init sub module failed\n"); | |
1239 | return -EPROBE_DEFER; | |
1240 | } | |
1241 | ||
8ede2ecc | 1242 | mutex_init(&dp->event_mutex); |
c943b494 CU |
1243 | g_dp_display = &dp->dp_display; |
1244 | ||
d13e36d7 AK |
1245 | /* Store DP audio handle inside DP display */ |
1246 | g_dp_display->dp_audio = dp->audio; | |
1247 | ||
158b9aa7 AK |
1248 | init_completion(&dp->audio_comp); |
1249 | ||
061eb621 AK |
1250 | platform_set_drvdata(pdev, g_dp_display); |
1251 | ||
c943b494 CU |
1252 | rc = component_add(&pdev->dev, &dp_display_comp_ops); |
1253 | if (rc) { | |
1254 | DRM_ERROR("component add failed, rc=%d\n", rc); | |
1255 | dp_display_deinit_sub_modules(dp); | |
1256 | } | |
1257 | ||
1258 | return rc; | |
1259 | } | |
1260 | ||
1261 | static int dp_display_remove(struct platform_device *pdev) | |
1262 | { | |
1263 | struct dp_display_private *dp; | |
1264 | ||
061eb621 AK |
1265 | dp = container_of(g_dp_display, |
1266 | struct dp_display_private, dp_display); | |
c943b494 CU |
1267 | |
1268 | dp_display_deinit_sub_modules(dp); | |
1269 | ||
1270 | component_del(&pdev->dev, &dp_display_comp_ops); | |
1271 | platform_set_drvdata(pdev, NULL); | |
1272 | ||
1273 | return 0; | |
1274 | } | |
1275 | ||
1276 | static int dp_pm_resume(struct device *dev) | |
1277 | { | |
19e52bcb KH |
1278 | struct platform_device *pdev = to_platform_device(dev); |
1279 | struct msm_dp *dp_display = platform_get_drvdata(pdev); | |
1280 | struct dp_display_private *dp; | |
e8a767e0 | 1281 | int sink_count = 0; |
19e52bcb KH |
1282 | |
1283 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
1284 | ||
1285 | mutex_lock(&dp->event_mutex); | |
1286 | ||
1287 | /* start from disconnected state */ | |
1288 | dp->hpd_state = ST_DISCONNECTED; | |
1289 | ||
1290 | /* turn on dp ctrl/phy */ | |
ea9f337c | 1291 | dp_display_host_init(dp, true); |
19e52bcb KH |
1292 | |
1293 | dp_catalog_ctrl_hpd_config(dp->catalog); | |
1294 | ||
e8a767e0 KH |
1295 | /* |
1296 | * set sink to normal operation mode -- D0 | |
1297 | * before dpcd read | |
1298 | */ | |
1299 | dp_link_psm_config(dp->link, &dp->panel->link_info, false); | |
1300 | ||
1301 | if (dp_catalog_link_is_connected(dp->catalog)) { | |
1302 | sink_count = drm_dp_read_sink_count(dp->aux); | |
1303 | if (sink_count < 0) | |
1304 | sink_count = 0; | |
1305 | } | |
19e52bcb | 1306 | |
e8a767e0 | 1307 | dp->link->sink_count = sink_count; |
d9aa6571 KH |
1308 | /* |
1309 | * can not declared display is connected unless | |
1310 | * HDMI cable is plugged in and sink_count of | |
1311 | * dongle become 1 | |
1312 | */ | |
e8a767e0 | 1313 | if (dp->link->sink_count) |
19e52bcb | 1314 | dp->dp_display.is_connected = true; |
62671d2e | 1315 | else |
19e52bcb | 1316 | dp->dp_display.is_connected = false; |
19e52bcb KH |
1317 | |
1318 | mutex_unlock(&dp->event_mutex); | |
1319 | ||
c943b494 CU |
1320 | return 0; |
1321 | } | |
1322 | ||
1323 | static int dp_pm_suspend(struct device *dev) | |
1324 | { | |
8ede2ecc | 1325 | struct platform_device *pdev = to_platform_device(dev); |
19e52bcb KH |
1326 | struct msm_dp *dp_display = platform_get_drvdata(pdev); |
1327 | struct dp_display_private *dp; | |
8ede2ecc | 1328 | |
19e52bcb | 1329 | dp = container_of(dp_display, struct dp_display_private, dp_display); |
8ede2ecc | 1330 | |
19e52bcb KH |
1331 | mutex_lock(&dp->event_mutex); |
1332 | ||
f591dbb5 KH |
1333 | if (dp->core_initialized == true) { |
1334 | /* mainlink enabled */ | |
1335 | if (dp_power_clk_status(dp->power, DP_CTRL_PM)) | |
1336 | dp_ctrl_off_link_stream(dp->ctrl); | |
1337 | ||
19e52bcb | 1338 | dp_display_host_deinit(dp); |
f591dbb5 | 1339 | } |
19e52bcb KH |
1340 | |
1341 | dp->hpd_state = ST_SUSPENDED; | |
1342 | ||
62671d2e KH |
1343 | /* host_init will be called at pm_resume */ |
1344 | dp->core_initialized = false; | |
1345 | ||
19e52bcb | 1346 | mutex_unlock(&dp->event_mutex); |
8ede2ecc | 1347 | |
c943b494 CU |
1348 | return 0; |
1349 | } | |
1350 | ||
1351 | static int dp_pm_prepare(struct device *dev) | |
1352 | { | |
1353 | return 0; | |
1354 | } | |
1355 | ||
1356 | static void dp_pm_complete(struct device *dev) | |
1357 | { | |
1358 | ||
1359 | } | |
1360 | ||
1361 | static const struct dev_pm_ops dp_pm_ops = { | |
1362 | .suspend = dp_pm_suspend, | |
1363 | .resume = dp_pm_resume, | |
1364 | .prepare = dp_pm_prepare, | |
1365 | .complete = dp_pm_complete, | |
1366 | }; | |
1367 | ||
1368 | static struct platform_driver dp_display_driver = { | |
1369 | .probe = dp_display_probe, | |
1370 | .remove = dp_display_remove, | |
1371 | .driver = { | |
1372 | .name = "msm-dp-display", | |
1373 | .of_match_table = dp_dt_match, | |
1374 | .suppress_bind_attrs = true, | |
1375 | .pm = &dp_pm_ops, | |
1376 | }, | |
1377 | }; | |
1378 | ||
1379 | int __init msm_dp_register(void) | |
1380 | { | |
1381 | int ret; | |
1382 | ||
1383 | ret = platform_driver_register(&dp_display_driver); | |
1384 | if (ret) | |
1385 | DRM_ERROR("Dp display driver register failed"); | |
1386 | ||
1387 | return ret; | |
1388 | } | |
1389 | ||
1390 | void __exit msm_dp_unregister(void) | |
1391 | { | |
1392 | platform_driver_unregister(&dp_display_driver); | |
1393 | } | |
1394 | ||
220b856a TS |
1395 | void msm_dp_irq_postinstall(struct msm_dp *dp_display) |
1396 | { | |
1397 | struct dp_display_private *dp; | |
1398 | ||
1399 | if (!dp_display) | |
1400 | return; | |
1401 | ||
1402 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
1403 | ||
8ede2ecc KH |
1404 | dp_hpd_event_setup(dp); |
1405 | ||
1406 | dp_add_event(dp, EV_HPD_INIT_SETUP, 0, 100); | |
220b856a TS |
1407 | } |
1408 | ||
f913454a AK |
1409 | void msm_dp_debugfs_init(struct msm_dp *dp_display, struct drm_minor *minor) |
1410 | { | |
1411 | struct dp_display_private *dp; | |
1412 | struct device *dev; | |
1413 | int rc; | |
1414 | ||
1415 | dp = container_of(dp_display, struct dp_display_private, dp_display); | |
1416 | dev = &dp->pdev->dev; | |
1417 | ||
1418 | dp->debug = dp_debug_get(dev, dp->panel, dp->usbpd, | |
1419 | dp->link, &dp->dp_display.connector, | |
1420 | minor); | |
1421 | if (IS_ERR(dp->debug)) { | |
1422 | rc = PTR_ERR(dp->debug); | |
1423 | DRM_ERROR("failed to initialize debug, rc = %d\n", rc); | |
1424 | dp->debug = NULL; | |
1425 | } | |
1426 | } | |
1427 | ||
c943b494 CU |
1428 | int msm_dp_modeset_init(struct msm_dp *dp_display, struct drm_device *dev, |
1429 | struct drm_encoder *encoder) | |
1430 | { | |
1431 | struct msm_drm_private *priv; | |
1432 | int ret; | |
1433 | ||
1434 | if (WARN_ON(!encoder) || WARN_ON(!dp_display) || WARN_ON(!dev)) | |
1435 | return -EINVAL; | |
1436 | ||
1437 | priv = dev->dev_private; | |
1438 | dp_display->drm_dev = dev; | |
1439 | ||
1440 | ret = dp_display_request_irq(dp_display); | |
1441 | if (ret) { | |
1442 | DRM_ERROR("request_irq failed, ret=%d\n", ret); | |
1443 | return ret; | |
1444 | } | |
1445 | ||
1446 | dp_display->encoder = encoder; | |
1447 | ||
1448 | dp_display->connector = dp_drm_connector_init(dp_display); | |
1449 | if (IS_ERR(dp_display->connector)) { | |
1450 | ret = PTR_ERR(dp_display->connector); | |
1451 | DRM_DEV_ERROR(dev->dev, | |
1452 | "failed to create dp connector: %d\n", ret); | |
1453 | dp_display->connector = NULL; | |
1454 | return ret; | |
1455 | } | |
1456 | ||
1457 | priv->connectors[priv->num_connectors++] = dp_display->connector; | |
1458 | return 0; | |
1459 | } | |
1460 | ||
1461 | int msm_dp_display_enable(struct msm_dp *dp, struct drm_encoder *encoder) | |
1462 | { | |
1463 | int rc = 0; | |
1464 | struct dp_display_private *dp_display; | |
8ede2ecc | 1465 | u32 state; |
c943b494 CU |
1466 | |
1467 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
1468 | if (!dp_display->dp_mode.drm_mode.clock) { | |
1469 | DRM_ERROR("invalid params\n"); | |
1470 | return -EINVAL; | |
1471 | } | |
1472 | ||
8ede2ecc KH |
1473 | mutex_lock(&dp_display->event_mutex); |
1474 | ||
62671d2e | 1475 | /* stop sentinel checking */ |
19e52bcb KH |
1476 | dp_del_event(dp_display, EV_CONNECT_PENDING_TIMEOUT); |
1477 | ||
c943b494 CU |
1478 | rc = dp_display_set_mode(dp, &dp_display->dp_mode); |
1479 | if (rc) { | |
1480 | DRM_ERROR("Failed to perform a mode set, rc=%d\n", rc); | |
8ede2ecc | 1481 | mutex_unlock(&dp_display->event_mutex); |
c943b494 CU |
1482 | return rc; |
1483 | } | |
1484 | ||
1485 | rc = dp_display_prepare(dp); | |
1486 | if (rc) { | |
1487 | DRM_ERROR("DP display prepare failed, rc=%d\n", rc); | |
8ede2ecc | 1488 | mutex_unlock(&dp_display->event_mutex); |
c943b494 CU |
1489 | return rc; |
1490 | } | |
1491 | ||
19e52bcb | 1492 | state = dp_display->hpd_state; |
8ede2ecc | 1493 | |
62671d2e | 1494 | if (state == ST_DISPLAY_OFF) |
ea9f337c | 1495 | dp_display_host_init(dp_display, true); |
c943b494 | 1496 | |
8ede2ecc KH |
1497 | dp_display_enable(dp_display, 0); |
1498 | ||
c943b494 CU |
1499 | rc = dp_display_post_enable(dp); |
1500 | if (rc) { | |
1501 | DRM_ERROR("DP display post enable failed, rc=%d\n", rc); | |
8ede2ecc | 1502 | dp_display_disable(dp_display, 0); |
c943b494 CU |
1503 | dp_display_unprepare(dp); |
1504 | } | |
8ede2ecc | 1505 | |
62671d2e KH |
1506 | /* manual kick off plug event to train link */ |
1507 | if (state == ST_DISPLAY_OFF) | |
8ede2ecc KH |
1508 | dp_add_event(dp_display, EV_IRQ_HPD_INT, 0, 0); |
1509 | ||
1510 | /* completed connection */ | |
19e52bcb | 1511 | dp_display->hpd_state = ST_CONNECTED; |
8ede2ecc KH |
1512 | |
1513 | mutex_unlock(&dp_display->event_mutex); | |
1514 | ||
c943b494 | 1515 | return rc; |
8ede2ecc KH |
1516 | } |
1517 | ||
1518 | int msm_dp_display_pre_disable(struct msm_dp *dp, struct drm_encoder *encoder) | |
1519 | { | |
1520 | struct dp_display_private *dp_display; | |
1521 | ||
1522 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
1523 | ||
1524 | dp_ctrl_push_idle(dp_display->ctrl); | |
1525 | ||
1526 | return 0; | |
c943b494 CU |
1527 | } |
1528 | ||
1529 | int msm_dp_display_disable(struct msm_dp *dp, struct drm_encoder *encoder) | |
1530 | { | |
1531 | int rc = 0; | |
8ede2ecc KH |
1532 | u32 state; |
1533 | struct dp_display_private *dp_display; | |
c943b494 | 1534 | |
8ede2ecc | 1535 | dp_display = container_of(dp, struct dp_display_private, dp_display); |
c943b494 | 1536 | |
8ede2ecc KH |
1537 | mutex_lock(&dp_display->event_mutex); |
1538 | ||
62671d2e | 1539 | /* stop sentinel checking */ |
19e52bcb KH |
1540 | dp_del_event(dp_display, EV_DISCONNECT_PENDING_TIMEOUT); |
1541 | ||
8ede2ecc | 1542 | dp_display_disable(dp_display, 0); |
c943b494 CU |
1543 | |
1544 | rc = dp_display_unprepare(dp); | |
1545 | if (rc) | |
1546 | DRM_ERROR("DP display unprepare failed, rc=%d\n", rc); | |
1547 | ||
19e52bcb | 1548 | state = dp_display->hpd_state; |
8ede2ecc KH |
1549 | if (state == ST_DISCONNECT_PENDING) { |
1550 | /* completed disconnection */ | |
19e52bcb | 1551 | dp_display->hpd_state = ST_DISCONNECTED; |
8ede2ecc | 1552 | } else { |
62671d2e | 1553 | dp_display->hpd_state = ST_DISPLAY_OFF; |
8ede2ecc KH |
1554 | } |
1555 | ||
1556 | mutex_unlock(&dp_display->event_mutex); | |
c943b494 CU |
1557 | return rc; |
1558 | } | |
1559 | ||
1560 | void msm_dp_display_mode_set(struct msm_dp *dp, struct drm_encoder *encoder, | |
1561 | struct drm_display_mode *mode, | |
1562 | struct drm_display_mode *adjusted_mode) | |
1563 | { | |
1564 | struct dp_display_private *dp_display; | |
1565 | ||
1566 | dp_display = container_of(dp, struct dp_display_private, dp_display); | |
1567 | ||
1568 | memset(&dp_display->dp_mode, 0x0, sizeof(struct dp_display_mode)); | |
1569 | ||
1570 | if (dp_display_check_video_test(dp)) | |
1571 | dp_display->dp_mode.bpp = dp_display_get_test_bpp(dp); | |
1572 | else /* Default num_components per pixel = 3 */ | |
1573 | dp_display->dp_mode.bpp = dp->connector->display_info.bpc * 3; | |
1574 | ||
1575 | if (!dp_display->dp_mode.bpp) | |
1576 | dp_display->dp_mode.bpp = 24; /* Default bpp */ | |
1577 | ||
1578 | drm_mode_copy(&dp_display->dp_mode.drm_mode, adjusted_mode); | |
1579 | ||
1580 | dp_display->dp_mode.v_active_low = | |
1581 | !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NVSYNC); | |
1582 | ||
1583 | dp_display->dp_mode.h_active_low = | |
1584 | !!(dp_display->dp_mode.drm_mode.flags & DRM_MODE_FLAG_NHSYNC); | |
1585 | } |