summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/msm/dp/dp_drm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/drm/msm/dp/dp_drm.c')
-rw-r--r--drivers/gpu/drm/msm/dp/dp_drm.c215
1 files changed, 54 insertions, 161 deletions
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.c b/drivers/gpu/drm/msm/dp/dp_drm.c
index 80f59cf99089..62d58b9c4647 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.c
+++ b/drivers/gpu/drm/msm/dp/dp_drm.c
@@ -6,40 +6,25 @@
#include <drm/drm_atomic_helper.h>
#include <drm/drm_atomic.h>
#include <drm/drm_bridge.h>
+#include <drm/drm_bridge_connector.h>
#include <drm/drm_crtc.h>
#include "msm_drv.h"
#include "msm_kms.h"
#include "dp_drm.h"
-
-struct msm_dp_bridge {
- struct drm_bridge bridge;
- struct msm_dp *dp_display;
-};
-
-#define to_dp_display(x) container_of((x), struct msm_dp_bridge, bridge)
-
-struct dp_connector {
- struct drm_connector base;
- struct msm_dp *dp_display;
-};
-#define to_dp_connector(x) container_of(x, struct dp_connector, base)
-
/**
- * dp_connector_detect - callback to determine if connector is connected
- * @conn: Pointer to drm connector structure
- * @force: Force detect setting from drm framework
- * Returns: Connector 'is connected' status
+ * dp_bridge_detect - callback to determine if connector is connected
+ * @bridge: Pointer to drm bridge structure
+ * Returns: Bridge's 'is connected' status
*/
-static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
- bool force)
+static enum drm_connector_status dp_bridge_detect(struct drm_bridge *bridge)
{
struct msm_dp *dp;
- dp = to_dp_connector(conn)->dp_display;
+ dp = to_dp_bridge(bridge)->dp_display;
- DRM_DEBUG_DP("is_connected = %s\n",
+ drm_dbg_dp(dp->drm_dev, "is_connected = %s\n",
(dp->is_connected) ? "true" : "false");
return (dp->is_connected) ? connector_status_connected :
@@ -47,173 +32,45 @@ static enum drm_connector_status dp_connector_detect(struct drm_connector *conn,
}
/**
- * dp_connector_get_modes - callback to add drm modes via drm_mode_probed_add()
+ * dp_bridge_get_modes - callback to add drm modes via drm_mode_probed_add()
+ * @bridge: Poiner to drm bridge
* @connector: Pointer to drm connector structure
* Returns: Number of modes added
*/
-static int dp_connector_get_modes(struct drm_connector *connector)
+static int dp_bridge_get_modes(struct drm_bridge *bridge, struct drm_connector *connector)
{
int rc = 0;
struct msm_dp *dp;
- struct dp_display_mode *dp_mode = NULL;
- struct drm_display_mode *m, drm_mode;
if (!connector)
return 0;
- dp = to_dp_connector(connector)->dp_display;
-
- dp_mode = kzalloc(sizeof(*dp_mode), GFP_KERNEL);
- if (!dp_mode)
- return 0;
+ dp = to_dp_bridge(bridge)->dp_display;
/* pluggable case assumes EDID is read when HPD */
if (dp->is_connected) {
- /*
- *The get_modes() function might return one mode that is stored
- * in dp_mode when compliance test is in progress. If not, the
- * return value is equal to the total number of modes supported
- * by the sink
- */
- rc = dp_display_get_modes(dp, dp_mode);
+ rc = dp_display_get_modes(dp);
if (rc <= 0) {
DRM_ERROR("failed to get DP sink modes, rc=%d\n", rc);
- kfree(dp_mode);
return rc;
}
- if (dp_mode->drm_mode.clock) { /* valid DP mode */
- memset(&drm_mode, 0x0, sizeof(drm_mode));
- drm_mode_copy(&drm_mode, &dp_mode->drm_mode);
- m = drm_mode_duplicate(connector->dev, &drm_mode);
- if (!m) {
- DRM_ERROR("failed to add mode %ux%u\n",
- drm_mode.hdisplay,
- drm_mode.vdisplay);
- kfree(dp_mode);
- return 0;
- }
- drm_mode_probed_add(connector, m);
- }
} else {
- DRM_DEBUG_DP("No sink connected\n");
+ drm_dbg_dp(connector->dev, "No sink connected\n");
}
- kfree(dp_mode);
return rc;
}
-/**
- * dp_connector_mode_valid - callback to determine if specified mode is valid
- * @connector: Pointer to drm connector structure
- * @mode: Pointer to drm mode structure
- * Returns: Validity status for specified mode
- */
-static enum drm_mode_status dp_connector_mode_valid(
- struct drm_connector *connector,
- struct drm_display_mode *mode)
-{
- struct msm_dp *dp_disp;
-
- dp_disp = to_dp_connector(connector)->dp_display;
-
- if ((dp_disp->max_pclk_khz <= 0) ||
- (dp_disp->max_pclk_khz > DP_MAX_PIXEL_CLK_KHZ) ||
- (mode->clock > dp_disp->max_pclk_khz))
- return MODE_BAD;
-
- return dp_display_validate_mode(dp_disp, mode->clock);
-}
-
-static const struct drm_connector_funcs dp_connector_funcs = {
- .detect = dp_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
-};
-
-static const struct drm_connector_helper_funcs dp_connector_helper_funcs = {
- .get_modes = dp_connector_get_modes,
- .mode_valid = dp_connector_mode_valid,
-};
-
-/* connector initialization */
-struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
-{
- struct drm_connector *connector = NULL;
- struct dp_connector *dp_connector;
- int ret;
-
- dp_connector = devm_kzalloc(dp_display->drm_dev->dev,
- sizeof(*dp_connector),
- GFP_KERNEL);
- if (!dp_connector)
- return ERR_PTR(-ENOMEM);
-
- dp_connector->dp_display = dp_display;
-
- connector = &dp_connector->base;
-
- ret = drm_connector_init(dp_display->drm_dev, connector,
- &dp_connector_funcs,
- dp_display->connector_type);
- if (ret)
- return ERR_PTR(ret);
-
- drm_connector_helper_add(connector, &dp_connector_helper_funcs);
-
- /*
- * Enable HPD to let hpd event is handled when cable is connected.
- */
- connector->polled = DRM_CONNECTOR_POLL_HPD;
-
- drm_connector_attach_encoder(connector, dp_display->encoder);
-
- return connector;
-}
-
-static void dp_bridge_mode_set(struct drm_bridge *drm_bridge,
- const struct drm_display_mode *mode,
- const struct drm_display_mode *adjusted_mode)
-{
- struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
- struct msm_dp *dp_display = dp_bridge->dp_display;
-
- msm_dp_display_mode_set(dp_display, drm_bridge->encoder, mode, adjusted_mode);
-}
-
-static void dp_bridge_enable(struct drm_bridge *drm_bridge)
-{
- struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
- struct msm_dp *dp_display = dp_bridge->dp_display;
-
- msm_dp_display_enable(dp_display, drm_bridge->encoder);
-}
-
-static void dp_bridge_disable(struct drm_bridge *drm_bridge)
-{
- struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
- struct msm_dp *dp_display = dp_bridge->dp_display;
-
- msm_dp_display_pre_disable(dp_display, drm_bridge->encoder);
-}
-
-static void dp_bridge_post_disable(struct drm_bridge *drm_bridge)
-{
- struct msm_dp_bridge *dp_bridge = to_dp_display(drm_bridge);
- struct msm_dp *dp_display = dp_bridge->dp_display;
-
- msm_dp_display_disable(dp_display, drm_bridge->encoder);
-}
-
static const struct drm_bridge_funcs dp_bridge_ops = {
.enable = dp_bridge_enable,
.disable = dp_bridge_disable,
.post_disable = dp_bridge_post_disable,
.mode_set = dp_bridge_mode_set,
+ .mode_valid = dp_bridge_mode_valid,
+ .get_modes = dp_bridge_get_modes,
+ .detect = dp_bridge_detect,
};
-struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
+struct drm_bridge *dp_bridge_init(struct msm_dp *dp_display, struct drm_device *dev,
struct drm_encoder *encoder)
{
int rc;
@@ -228,11 +85,33 @@ struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_devi
bridge = &dp_bridge->bridge;
bridge->funcs = &dp_bridge_ops;
- bridge->encoder = encoder;
+ bridge->type = dp_display->connector_type;
+
+ /*
+ * Many ops only make sense for DP. Why?
+ * - Detect/HPD are used by DRM to know if a display is _physically_
+ * there, not whether the display is powered on / finished initting.
+ * On eDP we assume the display is always there because you can't
+ * know until power is applied. If we don't implement the ops DRM will
+ * assume our display is always there.
+ * - Currently eDP mode reading is driven by the panel driver. This
+ * allows the panel driver to properly power itself on to read the
+ * modes.
+ */
+ if (!dp_display->is_edp) {
+ bridge->ops =
+ DRM_BRIDGE_OP_DETECT |
+ DRM_BRIDGE_OP_HPD |
+ DRM_BRIDGE_OP_MODES;
+ }
+
+ drm_bridge_add(bridge);
rc = drm_bridge_attach(encoder, bridge, NULL, DRM_BRIDGE_ATTACH_NO_CONNECTOR);
if (rc) {
DRM_ERROR("failed to attach bridge, rc=%d\n", rc);
+ drm_bridge_remove(bridge);
+
return ERR_PTR(rc);
}
@@ -249,3 +128,17 @@ struct drm_bridge *msm_dp_bridge_init(struct msm_dp *dp_display, struct drm_devi
return bridge;
}
+
+/* connector initialization */
+struct drm_connector *dp_drm_connector_init(struct msm_dp *dp_display)
+{
+ struct drm_connector *connector = NULL;
+
+ connector = drm_bridge_connector_init(dp_display->drm_dev, dp_display->encoder);
+ if (IS_ERR(connector))
+ return connector;
+
+ drm_connector_attach_encoder(connector, dp_display->encoder);
+
+ return connector;
+}