diff options
| author | Dave Airlie <airlied@redhat.com> | 2015-06-04 09:17:45 +1000 |
|---|---|---|
| committer | Dave Airlie <airlied@redhat.com> | 2015-06-04 09:17:45 +1000 |
| commit | 531e63e163072e325d9cc82acc095a009f07b6ef (patch) | |
| tree | b5b63cc6996b172658e3e1f2b0ce00d4aa686465 /drivers | |
| parent | cb2d47a47c59614821433b25ad3052181334aa18 (diff) | |
| parent | 5ceecb2fa720c01a97e6fa2353c2b8f1e8d69f1f (diff) | |
| download | linux-531e63e163072e325d9cc82acc095a009f07b6ef.tar.gz linux-531e63e163072e325d9cc82acc095a009f07b6ef.tar.bz2 linux-531e63e163072e325d9cc82acc095a009f07b6ef.zip | |
Merge tag 'topic/drm-misc-2015-05-27' of git://anongit.freedesktop.org/drm-intel into drm-next
One more round of drm-misc, again mostly atomic. Big thing is the
userspace blob code from Daniel Stone, with support for the mode_id blob
now added to the atomic ioctl. Finally we can do atomic modesets!
Note that the atomic ioctl is still behind the module knob since the
weston patches aren't quite ready yet imo - they lack TEST_ONLY support,
which is a fairly crucial bit of the atomic api. But besides that I think
it's all good to go. That's also why we didn't bother to hide the new blob
ioctls behind the knob, that part won't need to change. And if weston
patches get in shape in time we could throw the "atomic by default patch"
on top for 4.2.
* tag 'topic/drm-misc-2015-05-27' of git://anongit.freedesktop.org/drm-intel:
drm: Fix off-by-one in vblank hardware counter wraparound handling
drm/atomic: fix out of bounds read in for_each_*_in_state helpers
drm/atomic: Add MODE_ID property
drm/atomic: Add current-mode blob to CRTC state
drm: Add drm_atomic_set_mode_for_crtc
drm: check for garbage in unused addfb2 fields
drm: Retain reference to blob properties in lookup
drm/mode: Add user blob-creation ioctl
drm: Return error value from blob creation
drm: Allow creating blob properties without copy
drm/mode: Unstatic kernel-userspace mode conversion
drm/mode: Validate modes inside drm_crtc_convert_umode
drm/crtc_helper: Replace open-coded CRTC state helpers
drm: kerneldoc fixes for blob properties
drm/DocBook: Add more drm_bridge documentation
drm: bridge: Allow daisy chaining of bridges
drm/atomic: add all affected planes in drm_atomic_helper_check_modeset
drm/atomic: add drm_atomic_add_affected_planes
drm/atomic: add commit_planes_on_crtc helper
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/gpu/drm/drm_atomic.c | 162 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_atomic_helper.c | 117 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_bridge.c | 242 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_crtc.c | 304 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_crtc_helper.c | 101 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_fops.c | 5 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_ioctl.c | 2 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_irq.c | 4 | ||||
| -rw-r--r-- | drivers/gpu/drm/drm_modes.c | 87 |
9 files changed, 832 insertions, 192 deletions
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c index cd1b16b25716..c7e59b074e62 100644 --- a/drivers/gpu/drm/drm_atomic.c +++ b/drivers/gpu/drm/drm_atomic.c @@ -290,6 +290,100 @@ drm_atomic_get_crtc_state(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_get_crtc_state); /** + * drm_atomic_set_mode_for_crtc - set mode for CRTC + * @state: the CRTC whose incoming state to update + * @mode: kernel-internal mode to use for the CRTC, or NULL to disable + * + * Set a mode (originating from the kernel) on the desired CRTC state. Does + * not change any other state properties, including enable, active, or + * mode_changed. + * + * RETURNS: + * Zero on success, error code on failure. Cannot return -EDEADLK. + */ +int drm_atomic_set_mode_for_crtc(struct drm_crtc_state *state, + struct drm_display_mode *mode) +{ + struct drm_mode_modeinfo umode; + + /* Early return for no change. */ + if (mode && memcmp(&state->mode, mode, sizeof(*mode)) == 0) + return 0; + + if (state->mode_blob) + drm_property_unreference_blob(state->mode_blob); + state->mode_blob = NULL; + + if (mode) { + drm_mode_convert_to_umode(&umode, mode); + state->mode_blob = + drm_property_create_blob(state->crtc->dev, + sizeof(umode), + &umode); + if (IS_ERR(state->mode_blob)) + return PTR_ERR(state->mode_blob); + + drm_mode_copy(&state->mode, mode); + state->enable = true; + DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", + mode->name, state); + } else { + memset(&state->mode, 0, sizeof(state->mode)); + state->enable = false; + DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", + state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_mode_for_crtc); + +/** + * drm_atomic_set_mode_prop_for_crtc - set mode for CRTC + * @state: the CRTC whose incoming state to update + * @blob: pointer to blob property to use for mode + * + * Set a mode (originating from a blob property) on the desired CRTC state. + * This function will take a reference on the blob property for the CRTC state, + * and release the reference held on the state's existing mode property, if any + * was set. + * + * RETURNS: + * Zero on success, error code on failure. Cannot return -EDEADLK. + */ +int drm_atomic_set_mode_prop_for_crtc(struct drm_crtc_state *state, + struct drm_property_blob *blob) +{ + if (blob == state->mode_blob) + return 0; + + if (state->mode_blob) + drm_property_unreference_blob(state->mode_blob); + state->mode_blob = NULL; + + if (blob) { + if (blob->length != sizeof(struct drm_mode_modeinfo) || + drm_mode_convert_umode(&state->mode, + (const struct drm_mode_modeinfo *) + blob->data)) + return -EINVAL; + + state->mode_blob = drm_property_reference_blob(blob); + state->enable = true; + DRM_DEBUG_ATOMIC("Set [MODE:%s] for CRTC state %p\n", + state->mode.name, state); + } else { + memset(&state->mode, 0, sizeof(state->mode)); + state->enable = false; + DRM_DEBUG_ATOMIC("Set [NOMODE] for CRTC state %p\n", + state); + } + + return 0; +} +EXPORT_SYMBOL(drm_atomic_set_mode_prop_for_crtc); + +/** * drm_atomic_crtc_set_property - set property on CRTC * @crtc: the drm CRTC to set a property on * @state: the state object to update with the new property value @@ -311,10 +405,18 @@ int drm_atomic_crtc_set_property(struct drm_crtc *crtc, { struct drm_device *dev = crtc->dev; struct drm_mode_config *config = &dev->mode_config; + int ret; - /* FIXME: Mode prop is missing, which also controls ->enable. */ if (property == config->prop_active) state->active = val; + else if (property == config->prop_mode_id) { + struct drm_property_blob *mode = + drm_property_lookup_blob(dev, val); + ret = drm_atomic_set_mode_prop_for_crtc(state, mode); + if (mode) + drm_property_unreference_blob(mode); + return ret; + } else if (crtc->funcs->atomic_set_property) return crtc->funcs->atomic_set_property(crtc, state, property, val); else @@ -339,6 +441,8 @@ int drm_atomic_crtc_get_property(struct drm_crtc *crtc, if (property == config->prop_active) *val = state->active; + else if (property == config->prop_mode_id) + *val = (state->mode_blob) ? state->mode_blob->base.id : 0; else if (crtc->funcs->atomic_get_property) return crtc->funcs->atomic_get_property(crtc, state, property, val); else @@ -374,6 +478,23 @@ static int drm_atomic_crtc_check(struct drm_crtc *crtc, return -EINVAL; } + /* The state->enable vs. state->mode_blob checks can be WARN_ON, + * as this is a kernel-internal detail that userspace should never + * be able to trigger. */ + if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && + WARN_ON(state->enable && !state->mode_blob)) { + DRM_DEBUG_ATOMIC("[CRTC:%d] enabled without mode blob\n", + crtc->base.id); + return -EINVAL; + } + + if (drm_core_check_feature(crtc->dev, DRIVER_ATOMIC) && + WARN_ON(!state->enable && state->mode_blob)) { + DRM_DEBUG_ATOMIC("[CRTC:%d] disabled with mode blob\n", + crtc->base.id); + return -EINVAL; + } + return 0; } @@ -956,6 +1077,45 @@ drm_atomic_add_affected_connectors(struct drm_atomic_state *state, EXPORT_SYMBOL(drm_atomic_add_affected_connectors); /** + * drm_atomic_add_affected_planes - add planes for crtc + * @state: atomic state + * @crtc: DRM crtc + * + * This function walks the current configuration and adds all planes + * currently used by @crtc to the atomic configuration @state. This is useful + * when an atomic commit also needs to check all currently enabled plane on + * @crtc, e.g. when changing the mode. It's also useful when re-enabling a CRTC + * to avoid special code to force-enable all planes. + * + * Since acquiring a plane state will always also acquire the w/w mutex of the + * current CRTC for that plane (if there is any) adding all the plane states for + * a CRTC will not reduce parallism of atomic updates. + * + * Returns: + * 0 on success or can fail with -EDEADLK or -ENOMEM. When the error is EDEADLK + * then the w/w mutex code has detected a deadlock and the entire atomic + * sequence must be restarted. All other errors are fatal. + */ +int +drm_atomic_add_affected_planes(struct drm_atomic_state *state, + struct drm_crtc *crtc) +{ + struct drm_plane *plane; + + WARN_ON(!drm_atomic_get_existing_crtc_state(state, crtc)); + + drm_for_each_plane_mask(plane, state->dev, crtc->state->plane_mask) { + struct drm_plane_state *plane_state = + drm_atomic_get_plane_state(state, plane); + + if (IS_ERR(plane_state)) + return PTR_ERR(plane_state); + } + return 0; +} +EXPORT_SYMBOL(drm_atomic_add_affected_planes); + +/** * drm_atomic_connectors_for_crtc - count number of connected outputs * @state: atomic state * @crtc: DRM crtc diff --git a/drivers/gpu/drm/drm_atomic_helper.c b/drivers/gpu/drm/drm_atomic_helper.c index b82ef6262469..a900858fa265 100644 --- a/drivers/gpu/drm/drm_atomic_helper.c +++ b/drivers/gpu/drm/drm_atomic_helper.c @@ -283,14 +283,11 @@ mode_fixup(struct drm_atomic_state *state) 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) { @@ -429,6 +426,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); @@ -583,8 +584,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) @@ -594,8 +594,7 @@ 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) { @@ -737,9 +736,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); } } @@ -835,16 +832,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); @@ -1127,6 +1122,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) @@ -1181,6 +1180,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 @@ -1550,7 +1607,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); @@ -1565,9 +1625,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) @@ -1982,6 +2044,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); @@ -2003,6 +2067,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; @@ -2045,11 +2111,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); diff --git a/drivers/gpu/drm/drm_bridge.c b/drivers/gpu/drm/drm_bridge.c index eaa5790c2a6f..6b8f7211e543 100644 --- a/drivers/gpu/drm/drm_bridge.c +++ b/drivers/gpu/drm/drm_bridge.c @@ -28,9 +28,42 @@ #include "drm/drmP.h" +/** + * DOC: overview + * + * drm_bridge represents a device that hangs on to an encoder. These are handy + * when a regular drm_encoder entity isn't enough to represent the entire + * encoder chain. + * + * A bridge is always associated to a single drm_encoder at a time, but can be + * either connected to it directly, or through an intermediate bridge: + * + * encoder ---> bridge B ---> bridge A + * + * Here, the output of the encoder feeds to bridge B, and that furthers feeds to + * bridge A. + * + * The driver using the bridge is responsible to make the associations between + * the encoder and bridges. Once these links are made, the bridges will + * participate along with encoder functions to perform mode_set/enable/disable + * through the ops provided in drm_bridge_funcs. + * + * drm_bridge, like drm_panel, aren't drm_mode_object entities like planes, + * crtcs, encoders or connectors. They just provide additional hooks to get the + * desired output at the end of the encoder chain. + */ + static DEFINE_MUTEX(bridge_lock); static LIST_HEAD(bridge_list); +/** + * drm_bridge_add - add the given bridge to the global bridge list + * + * @bridge: bridge control structure + * + * RETURNS: + * Unconditionally returns Zero. + */ int drm_bridge_add(struct drm_bridge *bridge) { mutex_lock(&bridge_lock); @@ -41,6 +74,11 @@ int drm_bridge_add(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_add); +/** + * drm_bridge_remove - remove the given bridge from the global bridge list + * + * @bridge: bridge control structure + */ void drm_bridge_remove(struct drm_bridge *bridge) { mutex_lock(&bridge_lock); @@ -49,6 +87,21 @@ void drm_bridge_remove(struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_remove); +/** + * drm_bridge_attach - associate given bridge to our DRM device + * + * @dev: DRM device + * @bridge: bridge control structure + * + * called by a kms driver to link one of our encoder/bridge to the given + * bridge. + * + * Note that setting up links between the bridge and our encoder/bridge + * objects needs to be handled by the kms driver itself + * + * RETURNS: + * Zero on success, error code on failure + */ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) { if (!dev || !bridge) @@ -66,7 +119,196 @@ int drm_bridge_attach(struct drm_device *dev, struct drm_bridge *bridge) } EXPORT_SYMBOL(drm_bridge_attach); +/** + * DOC: bridge callbacks + * + * The drm_bridge_funcs ops are populated by the bridge driver. The drm + * internals(atomic and crtc helpers) use the helpers defined in drm_bridge.c + * These helpers call a specific drm_bridge_funcs op for all the bridges + * during encoder configuration. + * + * When creating a bridge driver, one can implement drm_bridge_funcs op with + * the help of these rough rules: + * + * pre_enable: this contains things needed to be done for the bridge before + * its clock and timings are enabled by its source. For a bridge, its source + * is generally the encoder or bridge just before it in the encoder chain. + * + * enable: this contains things needed to be done for the bridge once its + * source is enabled. In other words, enable is called once the source is + * ready with clock and timing needed by the bridge. + * + * disable: this contains things needed to be done for the bridge assuming + * that its source is still enabled, i.e. clock and timings are still on. + * + * post_disable: this contains things needed to be done for the bridge once + * its source is disabled, i.e. once clocks and timings are off. + * + * mode_fixup: this should fixup the given mode for the bridge. It is called + * after the encoder's mode fixup. mode_fixup can also reject a mode completely + * if it's unsuitable for the hardware. + * + * mode_set: this sets up the mode for the bridge. It assumes that its source + * (an encoder or a bridge) has set the mode too. + */ + +/** + * drm_bridge_mode_fixup - fixup proposed mode for all bridges in the + * encoder chain + * @bridge: bridge control structure + * @mode: desired mode to be set for the bridge + * @adjusted_mode: updated mode that works for this bridge + * + * Calls 'mode_fixup' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. + * + * Note: the bridge passed should be the one closest to the encoder + * + * RETURNS: + * true on success, false on failure + */ +bool drm_bridge_mode_fixup(struct drm_bridge *bridge, + const struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + bool ret = true; + + if (!bridge) + return true; + + if (bridge->funcs->mode_fixup) + ret = bridge->funcs->mode_fixup(bridge, mode, adjusted_mode); + + ret = ret && drm_bridge_mode_fixup(bridge->next, mode, adjusted_mode); + + return ret; +} +EXPORT_SYMBOL(drm_bridge_mode_fixup); + +/** + * drm_bridge_disable - calls 'disable' drm_bridge_funcs op for all + * bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'disable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called before + * calling the encoder's prepare op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_disable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + drm_bridge_disable(bridge->next); + + bridge->funcs->disable(bridge); +} +EXPORT_SYMBOL(drm_bridge_disable); + +/** + * drm_bridge_post_disable - calls 'post_disable' drm_bridge_funcs op for + * all bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'post_disable' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. These are called + * after completing the encoder's prepare op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_post_disable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + bridge->funcs->post_disable(bridge); + + drm_bridge_post_disable(bridge->next); +} +EXPORT_SYMBOL(drm_bridge_post_disable); + +/** + * drm_bridge_mode_set - set proposed mode for all bridges in the + * encoder chain + * @bridge: bridge control structure + * @mode: desired mode to be set for the bridge + * @adjusted_mode: updated mode that works for this bridge + * + * Calls 'mode_set' drm_bridge_funcs op for all the bridges in the + * encoder chain, starting from the first bridge to the last. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) +{ + if (!bridge) + return; + + if (bridge->funcs->mode_set) + bridge->funcs->mode_set(bridge, mode, adjusted_mode); + + drm_bridge_mode_set(bridge->next, mode, adjusted_mode); +} +EXPORT_SYMBOL(drm_bridge_mode_set); + +/** + * drm_bridge_pre_enable - calls 'pre_enable' drm_bridge_funcs op for all + * bridges in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'pre_enable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the last bridge to the first. These are called + * before calling the encoder's commit op. + * + * Note: the bridge passed should be the one closest to the encoder + */ +void drm_bridge_pre_enable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + drm_bridge_pre_enable(bridge->next); + + bridge->funcs->pre_enable(bridge); +} +EXPORT_SYMBOL(drm_bridge_pre_enable); + +/** + * drm_bridge_enable - calls 'enable' drm_bridge_funcs op for all bridges + * in the encoder chain. + * @bridge: bridge control structure + * + * Calls 'enable' drm_bridge_funcs op for all the bridges in the encoder + * chain, starting from the first bridge to the last. These are called + * after completing the encoder's commit op. + * + * Note that the bridge passed should be the one closest to the encoder + */ +void drm_bridge_enable(struct drm_bridge *bridge) +{ + if (!bridge) + return; + + bridge->funcs->enable(bridge); + + drm_bridge_enable(bridge->next); +} +EXPORT_SYMBOL(drm_bridge_enable); + #ifdef CONFIG_OF +/** + * of_drm_find_bridge - find the bridge corresponding to the device node in + * the global bridge list + * + * @np: device node + * + * RETURNS: + * drm_bridge control struct on success, NULL on failure + */ struct drm_bridge *of_drm_find_bridge(struct device_node *np) { struct drm_bridge *bridge; diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c index 4059f065c854..77f87b23a6e7 100644 --- a/drivers/gpu/drm/drm_crtc.c +++ b/drivers/gpu/drm/drm_crtc.c @@ -688,6 +688,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { drm_object_attach_property(&crtc->base, config->prop_active, 0); + drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); } return 0; @@ -1454,6 +1455,13 @@ static int drm_mode_create_standard_properties(struct drm_device *dev) return -ENOMEM; dev->mode_config.prop_active = prop; + prop = drm_property_create(dev, + DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, + "MODE_ID", 0); + if (!prop) + return -ENOMEM; + dev->mode_config.prop_mode_id = prop; + return 0; } @@ -1736,82 +1744,6 @@ void drm_reinit_primary_mode_group(struct drm_device *dev) EXPORT_SYMBOL(drm_reinit_primary_mode_group); /** - * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo - * @out: drm_mode_modeinfo struct to return to the user - * @in: drm_display_mode to use - * - * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to - * the user. - */ -static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, - const struct drm_display_mode *in) -{ - WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || - in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || - in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || - in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || - in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, - "timing values too large for mode info\n"); - - out->clock = in->clock; - out->hdisplay = in->hdisplay; - out->hsync_start = in->hsync_start; - out->hsync_end = in->hsync_end; - out->htotal = in->htotal; - out->hskew = in->hskew; - out->vdisplay = in->vdisplay; - out->vsync_start = in->vsync_start; - out->vsync_end = in->vsync_end; - out->vtotal = in->vtotal; - out->vscan = in->vscan; - out->vrefresh = in->vrefresh; - out->flags = in->flags; - out->type = in->type; - strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); - out->name[DRM_DISPLAY_MODE_LEN-1] = 0; -} - -/** - * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode - * @out: drm_display_mode to return to the user - * @in: drm_mode_modeinfo to use - * - * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to - * the caller. - * - * Returns: - * Zero on success, negative errno on failure. - */ -static int drm_crtc_convert_umode(struct drm_display_mode *out, - const struct drm_mode_modeinfo *in) -{ - if (in->clock > INT_MAX || in->vrefresh > INT_MAX) - return -ERANGE; - - if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) - return -EINVAL; - - out->clock = in->clock; - out->hdisplay = in->hdisplay; - out->hsync_start = in->hsync_start; - out->hsync_end = in->hsync_end; - out->htotal = in->htotal; - out->hskew = in->hskew; - out->vdisplay = in->vdisplay; - out->vsync_start = in->vsync_start; - out->vsync_end = in->vsync_end; - out->vtotal = in->vtotal; - out->vscan = in->vscan; - out->vrefresh = in->vrefresh; - out->flags = in->flags; - out->type = in->type; - strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); - out->name[DRM_DISPLAY_MODE_LEN-1] = 0; - - return 0; -} - -/** * drm_mode_getresources - get graphics configuration * @dev: drm device for the ioctl * @data: data pointer for the ioctl @@ -2037,7 +1969,7 @@ int drm_mode_getcrtc(struct drm_device *dev, crtc_resp->x = crtc->primary->state->src_x >> 16; crtc_resp->y = crtc->primary->state->src_y >> 16; if (crtc->state->enable) { - drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); + drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); crtc_resp->mode_valid = 1; } else { @@ -2047,7 +1979,7 @@ int drm_mode_getcrtc(struct drm_device *dev, crtc_resp->x = crtc->x; crtc_resp->y = crtc->y; if (crtc->enabled) { - drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); + drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); crtc_resp->mode_valid = 1; } else { @@ -2204,7 +2136,7 @@ int drm_mode_getconnector(struct drm_device *dev, void *data, if (!drm_mode_expose_to_userspace(mode, file_priv)) continue; - drm_crtc_convert_to_umode(&u_mode, mode); + drm_mode_convert_to_umode(&u_mode, mode); if (copy_to_user(mode_ptr + copied, &u_mode, sizeof(u_mode))) { ret = -EFAULT; @@ -2815,18 +2747,12 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data, goto out; } - ret = drm_crtc_convert_umode(mode, &crtc_req->mode); + ret = drm_mode_convert_umode(mode, &crtc_req->mode); if (ret) { DRM_DEBUG_KMS("Invalid mode\n"); goto out; } - mode->status = drm_mode_validate_basic(mode); - if (mode->status != MODE_OK) { - ret = -EINVAL; - goto out; - } - drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); /* @@ -3331,6 +3257,32 @@ static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) } } + for (i = num_planes; i < 4; i++) { + if (r->modifier[i]) { + DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); + return -EINVAL; + } + + /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ + if (!(r->flags & DRM_MODE_FB_MODIFIERS)) + continue; + + if (r->handles[i]) { + DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); + return -EINVAL; + } + + if (r->pitches[i]) { + DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); + return -EINVAL; + } + + if (r->offsets[i]) { + DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); + return -EINVAL; + } + } + return 0; } @@ -4227,6 +4179,20 @@ done: return ret; } +/** + * drm_property_create_blob - Create new blob property + * + * Creates a new blob property for a specified DRM device, optionally + * copying data. + * + * @dev: DRM device to create property for + * @length: Length to allocate for blob data + * @data: If specified, copies data into blob + * + * Returns: + * New blob property with a single reference on success, or an ERR_PTR + * value on failure. + */ struct drm_property_blob * drm_property_create_blob(struct drm_device *dev, size_t length, const void *data) @@ -4234,17 +4200,21 @@ drm_property_create_blob(struct drm_device *dev, size_t length, struct drm_property_blob *blob; int ret; - if (!length || !data) - return NULL; + if (!length) + return ERR_PTR(-EINVAL); blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); if (!blob) - return NULL; + return ERR_PTR(-ENOMEM); |
