summaryrefslogtreecommitdiff
path: root/drivers/gpu/drm/drm_atomic_helper.c
diff options
context:
space:
mode:
authorAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
committerAndré Fabian Silva Delgado <emulatorman@parabola.nu>2015-09-08 01:01:14 -0300
commite5fd91f1ef340da553f7a79da9540c3db711c937 (patch)
treeb11842027dc6641da63f4bcc524f8678263304a3 /drivers/gpu/drm/drm_atomic_helper.c
parent2a9b0348e685a63d97486f6749622b61e9e3292f (diff)
Linux-libre 4.2-gnu
Diffstat (limited to 'drivers/gpu/drm/drm_atomic_helper.c')
-rw-r--r--drivers/gpu/drm/drm_atomic_helper.c196
1 files changed, 146 insertions, 50 deletions
diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c
index 1d2ca5253..9dcc7280e 100644
--- a/drivers/gpu/drm/drm_atomic_helper.c
+++ b/drivers/gpu/drm/drm_atomic_helper.c
@@ -196,7 +196,12 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
}
funcs = connector->helper_private;
- new_encoder = funcs->best_encoder(connector);
+
+ if (funcs->atomic_best_encoder)
+ new_encoder = funcs->atomic_best_encoder(connector,
+ connector_state);
+ else
+ new_encoder = funcs->best_encoder(connector);
if (!new_encoder) {
DRM_DEBUG_ATOMIC("No suitable encoder found for [CONNECTOR:%d:%s]\n",
@@ -229,6 +234,9 @@ update_connector_routing(struct drm_atomic_state *state, int conn_idx)
}
}
+ if (WARN_ON(!connector_state->crtc))
+ return -EINVAL;
+
connector_state->best_encoder = new_encoder;
idx = drm_crtc_index(connector_state->crtc);
@@ -280,15 +288,14 @@ mode_fixup(struct drm_atomic_state *state)
*/
encoder = conn_state->best_encoder;
funcs = encoder->helper_private;
+ if (!funcs)
+ continue;
- if (encoder->bridge && encoder->bridge->funcs->mode_fixup) {
- ret = encoder->bridge->funcs->mode_fixup(
- encoder->bridge, &crtc_state->mode,
- &crtc_state->adjusted_mode);
- if (!ret) {
- DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
- return -EINVAL;
- }
+ ret = drm_bridge_mode_fixup(encoder->bridge, &crtc_state->mode,
+ &crtc_state->adjusted_mode);
+ if (!ret) {
+ DRM_DEBUG_ATOMIC("Bridge fixup failed\n");
+ return -EINVAL;
}
if (funcs->atomic_check) {
@@ -317,6 +324,9 @@ mode_fixup(struct drm_atomic_state *state)
continue;
funcs = crtc->helper_private;
+ if (!funcs->mode_fixup)
+ continue;
+
ret = funcs->mode_fixup(crtc, &crtc_state->mode,
&crtc_state->adjusted_mode);
if (!ret) {
@@ -329,12 +339,6 @@ mode_fixup(struct drm_atomic_state *state)
return 0;
}
-static bool
-needs_modeset(struct drm_crtc_state *state)
-{
- return state->mode_changed || state->active_changed;
-}
-
/**
* drm_atomic_helper_check_modeset - validate state object for modeset changes
* @dev: DRM device
@@ -412,7 +416,7 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
crtc_state->active_changed = true;
}
- if (!needs_modeset(crtc_state))
+ if (!drm_atomic_crtc_needs_modeset(crtc_state))
continue;
DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n",
@@ -424,6 +428,10 @@ drm_atomic_helper_check_modeset(struct drm_device *dev,
if (ret != 0)
return ret;
+ ret = drm_atomic_add_affected_planes(state, crtc);
+ if (ret != 0)
+ return ret;
+
num_connectors = drm_atomic_connectors_for_crtc(state,
crtc);
@@ -558,7 +566,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)];
if (!old_crtc_state->active ||
- !needs_modeset(old_conn_state->crtc->state))
+ !drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state))
continue;
encoder = old_conn_state->best_encoder;
@@ -578,8 +586,7 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
* Each encoder has at most one connector (since we always steal
* it away), so we won't call disable hooks twice.
*/
- if (encoder->bridge)
- encoder->bridge->funcs->disable(encoder->bridge);
+ drm_bridge_disable(encoder->bridge);
/* Right function depends upon target state. */
if (connector->state->crtc && funcs->prepare)
@@ -589,15 +596,14 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
else
funcs->dpms(encoder, DRM_MODE_DPMS_OFF);
- if (encoder->bridge)
- encoder->bridge->funcs->post_disable(encoder->bridge);
+ drm_bridge_post_disable(encoder->bridge);
}
for_each_crtc_in_state(old_state, crtc, old_crtc_state, i) {
const struct drm_crtc_helper_funcs *funcs;
/* Shut down everything that needs a full modeset. */
- if (!needs_modeset(crtc->state))
+ if (!drm_atomic_crtc_needs_modeset(crtc->state))
continue;
if (!old_crtc_state->active)
@@ -619,8 +625,22 @@ disable_outputs(struct drm_device *dev, struct drm_atomic_state *old_state)
}
}
-static void
-set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
+/**
+ * drm_atomic_helper_update_legacy_modeset_state - update legacy modeset state
+ * @dev: DRM device
+ * @old_state: atomic state object with old state structures
+ *
+ * This function updates all the various legacy modeset state pointers in
+ * connectors, encoders and crtcs. It also updates the timestamping constants
+ * used for precise vblank timestamps by calling
+ * drm_calc_timestamping_constants().
+ *
+ * Drivers can use this for building their own atomic commit if they don't have
+ * a pure helper-based modeset implementation.
+ */
+void
+drm_atomic_helper_update_legacy_modeset_state(struct drm_device *dev,
+ struct drm_atomic_state *old_state)
{
struct drm_connector *connector;
struct drm_connector_state *old_conn_state;
@@ -657,8 +677,13 @@ set_routing_links(struct drm_device *dev, struct drm_atomic_state *old_state)
crtc->enabled = crtc->state->enable;
crtc->x = crtc->primary->state->src_x >> 16;
crtc->y = crtc->primary->state->src_y >> 16;
+
+ if (crtc->state->enable)
+ drm_calc_timestamping_constants(crtc,
+ &crtc->state->adjusted_mode);
}
}
+EXPORT_SYMBOL(drm_atomic_helper_update_legacy_modeset_state);
static void
crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
@@ -713,9 +738,7 @@ crtc_set_mode(struct drm_device *dev, struct drm_atomic_state *old_state)
if (funcs->mode_set)
funcs->mode_set(encoder, mode, adjusted_mode);
- if (encoder->bridge && encoder->bridge->funcs->mode_set)
- encoder->bridge->funcs->mode_set(encoder->bridge,
- mode, adjusted_mode);
+ drm_bridge_mode_set(encoder->bridge, mode, adjusted_mode);
}
}
@@ -737,7 +760,9 @@ void drm_atomic_helper_commit_modeset_disables(struct drm_device *dev,
struct drm_atomic_state *old_state)
{
disable_outputs(dev, old_state);
- set_routing_links(dev, old_state);
+
+ drm_atomic_helper_update_legacy_modeset_state(dev, old_state);
+
crtc_set_mode(dev, old_state);
}
EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_disables);
@@ -769,7 +794,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
const struct drm_crtc_helper_funcs *funcs;
/* Need to filter out CRTCs where only planes change. */
- if (!needs_modeset(crtc->state))
+ if (!drm_atomic_crtc_needs_modeset(crtc->state))
continue;
if (!crtc->state->active)
@@ -796,7 +821,7 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
continue;
if (!connector->state->crtc->state->active ||
- !needs_modeset(connector->state->crtc->state))
+ !drm_atomic_crtc_needs_modeset(connector->state->crtc->state))
continue;
encoder = connector->state->best_encoder;
@@ -809,16 +834,14 @@ void drm_atomic_helper_commit_modeset_enables(struct drm_device *dev,
* Each encoder has at most one connector (since we always steal
* it away), so we won't call enable hooks twice.
*/
- if (encoder->bridge)
- encoder->bridge->funcs->pre_enable(encoder->bridge);
+ drm_bridge_pre_enable(encoder->bridge);
if (funcs->enable)
funcs->enable(encoder);
else
funcs->commit(encoder);
- if (encoder->bridge)
- encoder->bridge->funcs->enable(encoder->bridge);
+ drm_bridge_enable(encoder->bridge);
}
}
EXPORT_SYMBOL(drm_atomic_helper_commit_modeset_enables);
@@ -1101,6 +1124,10 @@ EXPORT_SYMBOL(drm_atomic_helper_prepare_planes);
*
* It still requires the global state object @old_state to know which planes and
* crtcs need to be updated though.
+ *
+ * Note that this function does all plane updates across all CRTCs in one step.
+ * If the hardware can't support this approach look at
+ * drm_atomic_helper_commit_planes_on_crtc() instead.
*/
void drm_atomic_helper_commit_planes(struct drm_device *dev,
struct drm_atomic_state *old_state)
@@ -1130,15 +1157,14 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
if (!funcs)
continue;
- old_plane_state = old_state->plane_states[i];
-
/*
* Special-case disabling the plane if drivers support it.
*/
if (drm_atomic_plane_disabling(plane, old_plane_state) &&
funcs->atomic_disable)
funcs->atomic_disable(plane, old_plane_state);
- else
+ else if (plane->state->crtc ||
+ drm_atomic_plane_disabling(plane, old_plane_state))
funcs->atomic_update(plane, old_plane_state);
}
@@ -1156,6 +1182,64 @@ void drm_atomic_helper_commit_planes(struct drm_device *dev,
EXPORT_SYMBOL(drm_atomic_helper_commit_planes);
/**
+ * drm_atomic_helper_commit_planes_on_crtc - commit plane state for a crtc
+ * @old_crtc_state: atomic state object with the old crtc state
+ *
+ * This function commits the new plane state using the plane and atomic helper
+ * functions for planes on the specific crtc. It assumes that the atomic state
+ * has already been pushed into the relevant object state pointers, since this
+ * step can no longer fail.
+ *
+ * This function is useful when plane updates should be done crtc-by-crtc
+ * instead of one global step like drm_atomic_helper_commit_planes() does.
+ *
+ * This function can only be savely used when planes are not allowed to move
+ * between different CRTCs because this function doesn't handle inter-CRTC
+ * depencies. Callers need to ensure that either no such depencies exist,
+ * resolve them through ordering of commit calls or through some other means.
+ */
+void
+drm_atomic_helper_commit_planes_on_crtc(struct drm_crtc_state *old_crtc_state)
+{
+ const struct drm_crtc_helper_funcs *crtc_funcs;
+ struct drm_crtc *crtc = old_crtc_state->crtc;
+ struct drm_atomic_state *old_state = old_crtc_state->state;
+ struct drm_plane *plane;
+ unsigned plane_mask;
+
+ plane_mask = old_crtc_state->plane_mask;
+ plane_mask |= crtc->state->plane_mask;
+
+ crtc_funcs = crtc->helper_private;
+ if (crtc_funcs && crtc_funcs->atomic_begin)
+ crtc_funcs->atomic_begin(crtc);
+
+ drm_for_each_plane_mask(plane, crtc->dev, plane_mask) {
+ struct drm_plane_state *old_plane_state =
+ drm_atomic_get_existing_plane_state(old_state, plane);
+ const struct drm_plane_helper_funcs *plane_funcs;
+
+ plane_funcs = plane->helper_private;
+
+ if (!old_plane_state || !plane_funcs)
+ continue;
+
+ WARN_ON(plane->state->crtc && plane->state->crtc != crtc);
+
+ if (drm_atomic_plane_disabling(plane, old_plane_state) &&
+ plane_funcs->atomic_disable)
+ plane_funcs->atomic_disable(plane, old_plane_state);
+ else if (plane->state->crtc ||
+ drm_atomic_plane_disabling(plane, old_plane_state))
+ plane_funcs->atomic_update(plane, old_plane_state);
+ }
+
+ if (crtc_funcs && crtc_funcs->atomic_flush)
+ crtc_funcs->atomic_flush(crtc);
+}
+EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc);
+
+/**
* drm_atomic_helper_cleanup_planes - cleanup plane resources after commit
* @dev: DRM device
* @old_state: atomic state object with old state structures
@@ -1309,13 +1393,13 @@ retry:
plane_state->src_h = src_h;
plane_state->src_w = src_w;
+ if (plane == crtc->cursor)
+ state->legacy_cursor_update = true;
+
ret = drm_atomic_commit(state);
if (ret != 0)
goto fail;
- if (plane == crtc->cursor)
- state->legacy_cursor_update = true;
-
/* Driver takes ownership of state on successful commit. */
return 0;
fail:
@@ -1479,8 +1563,14 @@ static int update_output_state(struct drm_atomic_state *state,
if (crtc == set->crtc)
continue;
- crtc_state->enable =
- drm_atomic_connectors_for_crtc(state, crtc);
+ if (!drm_atomic_connectors_for_crtc(state, crtc)) {
+ ret = drm_atomic_set_mode_prop_for_crtc(crtc_state,
+ NULL);
+ if (ret < 0)
+ return ret;
+
+ crtc_state->active = false;
+ }
}
return 0;
@@ -1525,7 +1615,10 @@ retry:
WARN_ON(set->fb);
WARN_ON(set->num_connectors);
- crtc_state->enable = false;
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, NULL);
+ if (ret != 0)
+ goto fail;
+
crtc_state->active = false;
ret = drm_atomic_set_crtc_for_plane(primary_state, NULL);
@@ -1540,9 +1633,11 @@ retry:
WARN_ON(!set->fb);
WARN_ON(!set->num_connectors);
- crtc_state->enable = true;
+ ret = drm_atomic_set_mode_for_crtc(crtc_state, set->mode);
+ if (ret != 0)
+ goto fail;
+
crtc_state->active = true;
- drm_mode_copy(&crtc_state->mode, set->mode);
ret = drm_atomic_set_crtc_for_plane(primary_state, crtc);
if (ret != 0)
@@ -1957,6 +2052,8 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_dpms);
*/
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc)
{
+ if (crtc->state && crtc->state->mode_blob)
+ drm_property_unreference_blob(crtc->state->mode_blob);
kfree(crtc->state);
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL);
@@ -1978,6 +2075,8 @@ void __drm_atomic_helper_crtc_duplicate_state(struct drm_crtc *crtc,
{
memcpy(state, crtc->state, sizeof(*state));
+ if (state->mode_blob)
+ drm_property_reference_blob(state->mode_blob);
state->mode_changed = false;
state->active_changed = false;
state->planes_changed = false;
@@ -2020,11 +2119,8 @@ EXPORT_SYMBOL(drm_atomic_helper_crtc_duplicate_state);
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc,
struct drm_crtc_state *state)
{
- /*
- * This is currently a placeholder so that drivers that subclass the
- * state will automatically do the right thing if code is ever added
- * to this function.
- */
+ if (state->mode_blob)
+ drm_property_unreference_blob(state->mode_blob);
}
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state);