GNU Linux-libre 5.19-rc6-gnu
[releases.git] / drivers / gpu / drm / msm / dp / dp_drm.c
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 }