xref: /openbmc/linux/drivers/gpu/drm/msm/dp/dp_drm.c (revision 0a94608f)
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_bridge_connector.h>
10 #include <drm/drm_crtc.h>
11 
12 #include "msm_drv.h"
13 #include "msm_kms.h"
14 #include "dp_drm.h"
15 
16 /**
17  * dp_bridge_detect - callback to determine if connector is connected
18  * @bridge: Pointer to drm bridge structure
19  * Returns: Bridge's 'is connected' status
20  */
21 static enum drm_connector_status dp_bridge_detect(struct drm_bridge *bridge)
22 {
23 	struct msm_dp *dp;
24 
25 	dp = to_dp_bridge(bridge)->dp_display;
26 
27 	drm_dbg_dp(dp->drm_dev, "is_connected = %s\n",
28 		(dp->is_connected) ? "true" : "false");
29 
30 	return (dp->is_connected) ? connector_status_connected :
31 					connector_status_disconnected;
32 }
33 
34 /**
35  * dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add()
36  * @bridge: Poiner to drm bridge
37  * @connector: Pointer to drm connector structure
38  * Returns: Number of modes added
39  */
40 static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector)
41 {
42 	int rc = 0;
43 	struct msm_dp *dp;
44 
45 	if (!connector)
46 		return 0;
47 
48 	dp = to_dp_bridge(bridge)->dp_display;
49 
50 	/* pluggable case assumes EDID is read when HPD */
51 	if (dp->is_connected) {
52 		rc = dp_display_get_modes(dp);
53 		if (rc <= 0) {
54 			DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
55 			return rc;
56 		}
57 	} else {
58 		drm_dbg_dp(connector->dev, "No sink connected\n");
59 	}
60 	return rc;
61 }
62 
63 static const struct drm_bridge_funcs dp_bridge_ops = {
64 	.enable       = dp_bridge_enable,
65 	.disable      = dp_bridge_disable,
66 	.post_disable = dp_bridge_post_disable,
67 	.mode_set     = dp_bridge_mode_set,
68 	.mode_valid   = dp_bridge_mode_valid,
69 	.get_modes    = dp_bridge_get_modes,
70 	.detect       = dp_bridge_detect,
71 };
72 
73 struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
74 			struct drm_encoder *encoder)
75 {
76 	int rc;
77 	struct msm_dp_bridge *dp_bridge;
78 	struct drm_bridge *bridge;
79 
80 	dp_bridge = devm_kzalloc(dev->dev, sizeof(*dp_bridge), GFP_KERNEL);
81 	if (!dp_bridge)
82 		return ERR_PTR(-ENOMEM);
83 
84 	dp_bridge->dp_display = dp_display;
85 
86 	bridge = &dp_bridge->bridge;
87 	bridge->funcs = &dp_bridge_ops;
88 	bridge->type = dp_display->connector_type;
89 
90 	/*
91 	 * Many ops only make sense for DP. Why?
92 	 * - Detect/HPD are used by DRM to know if a display is _physically_
93 	 *   there, not whether the display is powered on / finished initting.
94 	 *   On eDP we assume the display is always there because you can't
95 	 *   know until power is applied. If we don't implement the ops DRM will
96 	 *   assume our display is always there.
97 	 * - Currently eDP mode reading is driven by the panel driver. This
98 	 *   allows the panel driver to properly power itself on to read the
99 	 *   modes.
100 	 */
101 	if (!dp_display->is_edp) {
102 		bridge->ops =
103 			DRM_BRIDGE_OP_DETECT |
104 			DRM_BRIDGE_OP_HPD |
105 			DRM_BRIDGE_OP_MODES;
106 	}
107 
108 	drm_bridge_add(bridge);
109 
110 	rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
111 	if (rc) {
112 		DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
113 		drm_bridge_remove(bridge);
114 
115 		return ERR_PTR(rc);
116 	}
117 
118 	if (dp_display->next_bridge) {
119 		rc = drm_bridge_attach(dp_display->encoder,
120 					dp_display->next_bridge, bridge,
121 					DRM_BRIDGE_ATTACH_NO_CONNECTOR);
122 		if (rc < 0) {
123 			DRM_ERROR("failed to attach panel bridge: %d\n", rc);
124 			drm_bridge_remove(bridge);
125 			return ERR_PTR(rc);
126 		}
127 	}
128 
129 	return bridge;
130 }
131 
132 /* connector initialization */
133 struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
134 {
135 	struct drm_connector *connector = NULL;
136 
137 	connector = drm_bridge_connector_init(dp_display->drm_dev, dp_display->encoder);
138 	if (IS_ERR(connector))
139 		return connector;
140 
141 	drm_connector_attach_encoder(connector, dp_display->encoder);
142 
143 	return connector;
144 }
145