WIP FPC-III support
[linux/fpc-iii.git] / drivers / gpu / drm / msm / dp / dp_drm.c
blob764f4b81017edadc3659686907cdd77fe1d5ea67
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2017-2020, The Linux Foundation. All rights reserved.
4 */
6 #include <drm/drm_atomic_helper.h>
7 #include <drm/drm_atomic.h>
8 #include <drm/drm_crtc.h>
10 #include "msm_drv.h"
11 #include "msm_kms.h"
12 #include "dp_drm.h"
14 struct dp_connector {
15 struct drm_connector base;
16 struct msm_dp *dp_display;
18 #define to_dp_connector(x) container_of(x, struct dp_connector, base)
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
26 static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
27 bool force)
29 struct msm_dp *dp;
31 dp = to_dp_connector(conn)->dp_display;
33 DRM_DEBUG_DP("is_connected = %s\n",
34 (dp->is_connected) ? "true" : "false");
36 return (dp->is_connected) ? connector_status_connected :
37 connector_status_disconnected;
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
45 static int dp_connector_get_modes(struct drm_connector *connector)
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;
52 if (!connector)
53 return 0;
55 dp = to_dp_connector(connector)->dp_display;
57 dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL);
58 if (!dp_mode)
59 return 0;
61 /* pluggable case assumes EDID is read when HPD */
62 if (dp->is_connected) {
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
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;
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;
86 drm_mode_probed_add(connector, m);
88 } else {
89 DRM_DEBUG_DP("No sink connected\n");
91 kfree(dp_mode);
92 return rc;
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
101 static enum drm_mode_status dp_connector_mode_valid(
102 struct drm_connector *connector,
103 struct drm_display_mode *mode)
105 struct msm_dp *dp_disp;
107 dp_disp = to_dp_connector(connector)->dp_display;
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;
114 return dp_display_validate_mode(dp_disp, mode->clock);
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,
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,
131 /* connector initialization */
132 struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
134 struct drm_connector *connector = NULL;
135 struct dp_connector *dp_connector;
136 int ret;
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);
144 dp_connector->dp_display = dp_display;
146 connector = &dp_connector->base;
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);
154 drm_connector_helper_add(connector, &dp_connector_helper_funcs);
157 * Enable HPD to let hpd event is handled when cable is connected.
159 connector->polled = DRM_CONNECTOR_POLL_HPD;
161 drm_connector_attach_encoder(connector, dp_display->encoder);
163 return connector;