]> Git Repo - linux.git/blob - drivers/gpu/drm/msm/dp/dp_drm.c
Merge tag 'locking-urgent-2020-11-01' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.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_crtc.h>
9
10 #include "msm_drv.h"
11 #include "msm_kms.h"
12 #include "dp_drm.h"
13
14 struct dp_connector {
15         struct drm_connector base;
16         struct msm_dp *dp_display;
17 };
18 #define to_dp_connector(x) container_of(x, struct dp_connector, base)
19
20 /**
21  * dp_connector_detect - callback to determine if connector is connected
22  * @conn: Pointer to drm connector structure
23  * @force: Force detect setting from drm framework
24  * Returns: Connector 'is connected' status
25  */
26 static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
27                 bool force)
28 {
29         struct msm_dp *dp;
30
31         dp = to_dp_connector(conn)->dp_display;
32
33         DRM_DEBUG_DP("is_connected = %s\n",
34                 (dp->is_connected) ? "true" : "false");
35
36         return (dp->is_connected) ? connector_status_connected :
37                                         connector_status_disconnected;
38 }
39
40 /**
41  * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
42  * @connector: Pointer to drm connector structure
43  * Returns: Number of modes added
44  */
45 static int dp_connector_get_modes(struct drm_connector *connector)
46 {
47         int rc = 0;
48         struct msm_dp *dp;
49         struct dp_display_mode *dp_mode = NULL;
50         struct drm_display_mode *m, drm_mode;
51
52         if (!connector)
53                 return 0;
54
55         dp = to_dp_connector(connector)->dp_display;
56
57         dp_mode = kzalloc(sizeof(*dp_mode),  GFP_KERNEL);
58         if (!dp_mode)
59                 return 0;
60
61         /* pluggable case assumes EDID is read when HPD */
62         if (dp->is_connected) {
63                 /*
64                  *The get_modes() function might return one mode that is stored
65                  * in dp_mode when compliance test is in progress. If not, the
66                  * return value is equal to the total number of modes supported
67                  * by the sink
68                  */
69                 rc = dp_display_get_modes(dp, dp_mode);
70                 if (rc <= 0) {
71                         DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
72                         kfree(dp_mode);
73                         return rc;
74                 }
75                 if (dp_mode->drm_mode.clock) { /* valid DP mode */
76                         memset(&drm_mode, 0x0, sizeof(drm_mode));
77                         drm_mode_copy(&drm_mode, &dp_mode->drm_mode);
78                         m = drm_mode_duplicate(connector->dev, &drm_mode);
79                         if (!m) {
80                                 DRM_ERROR("failed to add mode %ux%u\n",
81                                        drm_mode.hdisplay,
82                                        drm_mode.vdisplay);
83                                 kfree(dp_mode);
84                                 return 0;
85                         }
86                         drm_mode_probed_add(connector, m);
87                 }
88         } else {
89                 DRM_DEBUG_DP("No sink connected\n");
90         }
91         kfree(dp_mode);
92         return rc;
93 }
94
95 /**
96  * dp_connector_mode_valid - callback to determine if specified mode is valid
97  * @connector: Pointer to drm connector structure
98  * @mode: Pointer to drm mode structure
99  * Returns: Validity status for specified mode
100  */
101 static enum drm_mode_status dp_connector_mode_valid(
102                 struct drm_connector *connector,
103                 struct drm_display_mode *mode)
104 {
105         struct msm_dp *dp_disp;
106
107         dp_disp = to_dp_connector(connector)->dp_display;
108
109         if ((dp_disp->max_pclk_khz <= 0) ||
110                         (dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) ||
111                         (mode->clock > dp_disp->max_pclk_khz))
112                 return MODE_BAD;
113
114         return dp_display_validate_mode(dp_disp, mode->clock);
115 }
116
117 static const struct drm_connector_funcs dp_connector_funcs = {
118         .detect = dp_connector_detect,
119         .fill_modes = drm_helper_probe_single_connector_modes,
120         .destroy = drm_connector_cleanup,
121         .reset = drm_atomic_helper_connector_reset,
122         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
123         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
124 };
125
126 static const struct drm_connector_helper_funcs dp_connector_helper_funcs = {
127         .get_modes = dp_connector_get_modes,
128         .mode_valid = dp_connector_mode_valid,
129 };
130
131 /* connector initialization */
132 struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
133 {
134         struct drm_connector *connector = NULL;
135         struct dp_connector *dp_connector;
136         int ret;
137
138         dp_connector = devm_kzalloc(dp_display->drm_dev->dev,
139                                         sizeof(*dp_connector),
140                                         GFP_KERNEL);
141         if (!dp_connector)
142                 return ERR_PTR(-ENOMEM);
143
144         dp_connector->dp_display = dp_display;
145
146         connector = &dp_connector->base;
147
148         ret = drm_connector_init(dp_display->drm_dev, connector,
149                         &dp_connector_funcs,
150                         DRM_MODE_CONNECTOR_DisplayPort);
151         if (ret)
152                 return ERR_PTR(ret);
153
154         drm_connector_helper_add(connector, &dp_connector_helper_funcs);
155
156         /*
157          * Enable HPD to let hpd event is handled when cable is connected.
158          */
159         connector->polled = DRM_CONNECTOR_POLL_HPD;
160
161         drm_connector_attach_encoder(connector, dp_display->encoder);
162
163         return connector;
164 }
This page took 0.044308 seconds and 4 git commands to generate.