Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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 */
21static 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 */
40static 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
63static 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
73struct 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(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 */
133struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display, struct drm_encoder *encoder)
134{
135 struct drm_connector *connector = NULL;
136
137 connector = drm_bridge_connector_init(dp_display->drm_dev, encoder);
138 if (IS_ERR(connector))
139 return connector;
140
141 drm_connector_attach_encoder(connector, encoder);
142
143 return connector;
144}