52,6 → 52,12 |
* drm_atomic_helper_disable_plane(), drm_atomic_helper_disable_plane() and the |
* various functions to implement set_property callbacks. New drivers must not |
* implement these functions themselves but must use the provided helpers. |
* |
* The atomic helper uses the same function table structures as all other |
* modesetting helpers. See the documentation for struct &drm_crtc_helper_funcs, |
* struct &drm_encoder_helper_funcs and struct &drm_connector_helper_funcs. It |
* also shares the struct &drm_plane_helper_funcs function table with the plane |
* helpers. |
*/ |
static void |
drm_atomic_helper_plane_changed(struct drm_atomic_state *state, |
80,6 → 86,26 |
} |
} |
|
static bool |
check_pending_encoder_assignment(struct drm_atomic_state *state, |
struct drm_encoder *new_encoder) |
{ |
struct drm_connector *connector; |
struct drm_connector_state *conn_state; |
int i; |
|
for_each_connector_in_state(state, connector, conn_state, i) { |
if (conn_state->best_encoder != new_encoder) |
continue; |
|
/* encoder already assigned and we're trying to re-steal it! */ |
if (connector->state->best_encoder != conn_state->best_encoder) |
return false; |
} |
|
return true; |
} |
|
static struct drm_crtc * |
get_current_crtc_for_encoder(struct drm_device *dev, |
struct drm_encoder *encoder) |
108,6 → 134,7 |
struct drm_crtc_state *crtc_state; |
struct drm_connector *connector; |
struct drm_connector_state *connector_state; |
int ret; |
|
/* |
* We can only steal an encoder coming from a connector, which means we |
115,9 → 142,9 |
*/ |
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); |
|
DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d], stealing it\n", |
DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], stealing it\n", |
encoder->base.id, encoder->name, |
encoder_crtc->base.id); |
encoder_crtc->base.id, encoder_crtc->name); |
|
crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); |
if (IS_ERR(crtc_state)) |
138,6 → 165,9 |
if (IS_ERR(connector_state)) |
return PTR_ERR(connector_state); |
|
ret = drm_atomic_set_crtc_for_connector(connector_state, NULL); |
if (ret) |
return ret; |
connector_state->best_encoder = NULL; |
} |
|
215,16 → 245,24 |
} |
|
if (new_encoder == connector_state->best_encoder) { |
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d]\n", |
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n", |
connector->base.id, |
connector->name, |
new_encoder->base.id, |
new_encoder->name, |
connector_state->crtc->base.id); |
connector_state->crtc->base.id, |
connector_state->crtc->name); |
|
return 0; |
} |
|
if (!check_pending_encoder_assignment(state, new_encoder)) { |
DRM_DEBUG_ATOMIC("Encoder for [CONNECTOR:%d:%s] already assigned\n", |
connector->base.id, |
connector->name); |
return -EINVAL; |
} |
|
encoder_crtc = get_current_crtc_for_encoder(state->dev, |
new_encoder); |
|
247,12 → 285,13 |
crtc_state = state->crtc_states[idx]; |
crtc_state->connectors_changed = true; |
|
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d]\n", |
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n", |
connector->base.id, |
connector->name, |
new_encoder->base.id, |
new_encoder->name, |
connector_state->crtc->base.id); |
connector_state->crtc->base.id, |
connector_state->crtc->name); |
|
return 0; |
} |
265,7 → 304,7 |
struct drm_connector *connector; |
struct drm_connector_state *conn_state; |
int i; |
int ret; |
bool ret; |
|
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
if (!crtc_state->mode_changed && |
336,8 → 375,8 |
ret = funcs->mode_fixup(crtc, &crtc_state->mode, |
&crtc_state->adjusted_mode); |
if (!ret) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] fixup failed\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] fixup failed\n", |
crtc->base.id, crtc->name); |
return -EINVAL; |
} |
} |
384,14 → 423,14 |
|
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
if (!drm_mode_equal(&crtc->state->mode, &crtc_state->mode)) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] mode changed\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] mode changed\n", |
crtc->base.id, crtc->name); |
crtc_state->mode_changed = true; |
} |
|
if (crtc->state->enable != crtc_state->enable) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] enable changed\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enable changed\n", |
crtc->base.id, crtc->name); |
|
/* |
* For clarity this assignment is done here, but |
424,7 → 463,8 |
* crtc only changed its mode but has the same set of connectors. |
*/ |
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
int num_connectors; |
bool has_connectors = |
!!crtc_state->connector_mask; |
|
/* |
* We must set ->active_changed after walking connectors for |
432,8 → 472,8 |
* a full modeset because update_connector_routing force that. |
*/ |
if (crtc->state->active != crtc_state->active) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] active changed\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] active changed\n", |
crtc->base.id, crtc->name); |
crtc_state->active_changed = true; |
} |
|
440,8 → 480,8 |
if (!drm_atomic_crtc_needs_modeset(crtc_state)) |
continue; |
|
DRM_DEBUG_ATOMIC("[CRTC:%d] needs all connectors, enable: %c, active: %c\n", |
crtc->base.id, |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] needs all connectors, enable: %c, active: %c\n", |
crtc->base.id, crtc->name, |
crtc_state->enable ? 'y' : 'n', |
crtc_state->active ? 'y' : 'n'); |
|
453,13 → 493,10 |
if (ret != 0) |
return ret; |
|
num_connectors = drm_atomic_connectors_for_crtc(state, |
crtc); |
if (crtc_state->enable != has_connectors) { |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] enabled/connectors mismatch\n", |
crtc->base.id, crtc->name); |
|
if (crtc_state->enable != !!num_connectors) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] enabled/connectors mismatch\n", |
crtc->base.id); |
|
return -EINVAL; |
} |
} |
505,8 → 542,8 |
|
ret = funcs->atomic_check(plane, plane_state); |
if (ret) { |
DRM_DEBUG_ATOMIC("[PLANE:%d] atomic driver check failed\n", |
plane->base.id); |
DRM_DEBUG_ATOMIC("[PLANE:%d:%s] atomic driver check failed\n", |
plane->base.id, plane->name); |
return ret; |
} |
} |
521,8 → 558,8 |
|
ret = funcs->atomic_check(crtc, state->crtc_states[i]); |
if (ret) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] atomic driver check failed\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("[CRTC:%d:%s] atomic driver check failed\n", |
crtc->base.id, crtc->name); |
return ret; |
} |
} |
635,8 → 672,8 |
|
funcs = crtc->helper_private; |
|
DRM_DEBUG_ATOMIC("disabling [CRTC:%d]\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("disabling [CRTC:%d:%s]\n", |
crtc->base.id, crtc->name); |
|
|
/* Right function depends upon target state. */ |
747,8 → 784,8 |
funcs = crtc->helper_private; |
|
if (crtc->state->enable && funcs->mode_set_nofb) { |
DRM_DEBUG_ATOMIC("modeset on [CRTC:%d]\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("modeset on [CRTC:%d:%s]\n", |
crtc->base.id, crtc->name); |
|
funcs->mode_set_nofb(crtc); |
} |
847,8 → 884,8 |
funcs = crtc->helper_private; |
|
if (crtc->state->enable) { |
DRM_DEBUG_ATOMIC("enabling [CRTC:%d]\n", |
crtc->base.id); |
DRM_DEBUG_ATOMIC("enabling [CRTC:%d:%s]\n", |
crtc->base.id, crtc->name); |
|
if (funcs->enable) |
funcs->enable(crtc); |
909,7 → 946,21 |
} |
} |
|
static bool framebuffer_changed(struct drm_device *dev, |
/** |
* drm_atomic_helper_framebuffer_changed - check if framebuffer has changed |
* @dev: DRM device |
* @old_state: atomic state object with old state structures |
* @crtc: DRM crtc |
* |
* Checks whether the framebuffer used for this CRTC changes as a result of |
* the atomic update. This is useful for drivers which cannot use |
* drm_atomic_helper_wait_for_vblanks() and need to reimplement its |
* functionality. |
* |
* Returns: |
* true if the framebuffer changed. |
*/ |
bool drm_atomic_helper_framebuffer_changed(struct drm_device *dev, |
struct drm_atomic_state *old_state, |
struct drm_crtc *crtc) |
{ |
928,6 → 979,7 |
|
return false; |
} |
EXPORT_SYMBOL(drm_atomic_helper_framebuffer_changed); |
|
/** |
* drm_atomic_helper_wait_for_vblanks - wait for vblank on crtcs |
962,7 → 1014,8 |
if (old_state->legacy_cursor_update) |
continue; |
|
if (!framebuffer_changed(dev, old_state, crtc)) |
if (!drm_atomic_helper_framebuffer_changed(dev, |
old_state, crtc)) |
continue; |
|
ret = drm_crtc_vblank_get(crtc); |
1338,6 → 1391,49 |
EXPORT_SYMBOL(drm_atomic_helper_commit_planes_on_crtc); |
|
/** |
* drm_atomic_helper_disable_planes_on_crtc - helper to disable CRTC's planes |
* @crtc: CRTC |
* @atomic: if set, synchronize with CRTC's atomic_begin/flush hooks |
* |
* Disables all planes associated with the given CRTC. This can be |
* used for instance in the CRTC helper disable callback to disable |
* all planes before shutting down the display pipeline. |
* |
* If the atomic-parameter is set the function calls the CRTC's |
* atomic_begin hook before and atomic_flush hook after disabling the |
* planes. |
* |
* It is a bug to call this function without having implemented the |
* ->atomic_disable() plane hook. |
*/ |
void drm_atomic_helper_disable_planes_on_crtc(struct drm_crtc *crtc, |
bool atomic) |
{ |
const struct drm_crtc_helper_funcs *crtc_funcs = |
crtc->helper_private; |
struct drm_plane *plane; |
|
if (atomic && crtc_funcs && crtc_funcs->atomic_begin) |
crtc_funcs->atomic_begin(crtc, NULL); |
|
drm_for_each_plane(plane, crtc->dev) { |
const struct drm_plane_helper_funcs *plane_funcs = |
plane->helper_private; |
|
if (plane->state->crtc != crtc || !plane_funcs) |
continue; |
|
WARN_ON(!plane_funcs->atomic_disable); |
if (plane_funcs->atomic_disable) |
plane_funcs->atomic_disable(plane, NULL); |
} |
|
if (atomic && crtc_funcs && crtc_funcs->atomic_flush) |
crtc_funcs->atomic_flush(crtc, NULL); |
} |
EXPORT_SYMBOL(drm_atomic_helper_disable_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 |
1397,7 → 1493,7 |
{ |
int i; |
|
for (i = 0; i < dev->mode_config.num_connector; i++) { |
for (i = 0; i < state->num_connector; i++) { |
struct drm_connector *connector = state->connectors[i]; |
|
if (!connector) |
1481,12 → 1577,12 |
drm_atomic_set_fb_for_plane(plane_state, fb); |
plane_state->crtc_x = crtc_x; |
plane_state->crtc_y = crtc_y; |
plane_state->crtc_w = crtc_w; |
plane_state->crtc_h = crtc_h; |
plane_state->crtc_w = crtc_w; |
plane_state->src_x = src_x; |
plane_state->src_y = src_y; |
plane_state->src_w = src_w; |
plane_state->src_h = src_h; |
plane_state->src_w = src_w; |
|
if (plane == crtc->cursor) |
state->legacy_cursor_update = true; |
1605,12 → 1701,12 |
drm_atomic_set_fb_for_plane(plane_state, NULL); |
plane_state->crtc_x = 0; |
plane_state->crtc_y = 0; |
plane_state->crtc_w = 0; |
plane_state->crtc_h = 0; |
plane_state->crtc_w = 0; |
plane_state->src_x = 0; |
plane_state->src_y = 0; |
plane_state->src_w = 0; |
plane_state->src_h = 0; |
plane_state->src_w = 0; |
|
return 0; |
} |
1672,7 → 1768,7 |
if (crtc == set->crtc) |
continue; |
|
if (!drm_atomic_connectors_for_crtc(state, crtc)) { |
if (!crtc_state->connector_mask) { |
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, |
NULL); |
if (ret < 0) |
1793,16 → 1889,16 |
drm_atomic_set_fb_for_plane(primary_state, set->fb); |
primary_state->crtc_x = 0; |
primary_state->crtc_y = 0; |
primary_state->crtc_w = hdisplay; |
primary_state->crtc_h = vdisplay; |
primary_state->crtc_w = hdisplay; |
primary_state->src_x = set->x << 16; |
primary_state->src_y = set->y << 16; |
if (primary_state->rotation & (BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_270))) { |
primary_state->src_w = vdisplay << 16; |
primary_state->src_h = hdisplay << 16; |
primary_state->src_w = vdisplay << 16; |
} else { |
primary_state->src_w = hdisplay << 16; |
primary_state->src_h = vdisplay << 16; |
primary_state->src_w = hdisplay << 16; |
} |
|
commit: |
1814,6 → 1910,161 |
} |
|
/** |
* drm_atomic_helper_disable_all - disable all currently active outputs |
* @dev: DRM device |
* @ctx: lock acquisition context |
* |
* Loops through all connectors, finding those that aren't turned off and then |
* turns them off by setting their DPMS mode to OFF and deactivating the CRTC |
* that they are connected to. |
* |
* This is used for example in suspend/resume to disable all currently active |
* functions when suspending. |
* |
* Note that if callers haven't already acquired all modeset locks this might |
* return -EDEADLK, which must be handled by calling drm_modeset_backoff(). |
* |
* Returns: |
* 0 on success or a negative error code on failure. |
* |
* See also: |
* drm_atomic_helper_suspend(), drm_atomic_helper_resume() |
*/ |
int drm_atomic_helper_disable_all(struct drm_device *dev, |
struct drm_modeset_acquire_ctx *ctx) |
{ |
struct drm_atomic_state *state; |
struct drm_connector *conn; |
int err; |
|
state = drm_atomic_state_alloc(dev); |
if (!state) |
return -ENOMEM; |
|
state->acquire_ctx = ctx; |
|
drm_for_each_connector(conn, dev) { |
struct drm_crtc *crtc = conn->state->crtc; |
struct drm_crtc_state *crtc_state; |
|
if (!crtc || conn->dpms != DRM_MODE_DPMS_ON) |
continue; |
|
crtc_state = drm_atomic_get_crtc_state(state, crtc); |
if (IS_ERR(crtc_state)) { |
err = PTR_ERR(crtc_state); |
goto free; |
} |
|
crtc_state->active = false; |
} |
|
err = drm_atomic_commit(state); |
|
free: |
if (err < 0) |
drm_atomic_state_free(state); |
|
return err; |
} |
EXPORT_SYMBOL(drm_atomic_helper_disable_all); |
|
/** |
* drm_atomic_helper_suspend - subsystem-level suspend helper |
* @dev: DRM device |
* |
* Duplicates the current atomic state, disables all active outputs and then |
* returns a pointer to the original atomic state to the caller. Drivers can |
* pass this pointer to the drm_atomic_helper_resume() helper upon resume to |
* restore the output configuration that was active at the time the system |
* entered suspend. |
* |
* Note that it is potentially unsafe to use this. The atomic state object |
* returned by this function is assumed to be persistent. Drivers must ensure |
* that this holds true. Before calling this function, drivers must make sure |
* to suspend fbdev emulation so that nothing can be using the device. |
* |
* Returns: |
* A pointer to a copy of the state before suspend on success or an ERR_PTR()- |
* encoded error code on failure. Drivers should store the returned atomic |
* state object and pass it to the drm_atomic_helper_resume() helper upon |
* resume. |
* |
* See also: |
* drm_atomic_helper_duplicate_state(), drm_atomic_helper_disable_all(), |
* drm_atomic_helper_resume() |
*/ |
struct drm_atomic_state *drm_atomic_helper_suspend(struct drm_device *dev) |
{ |
struct drm_modeset_acquire_ctx ctx; |
struct drm_atomic_state *state; |
int err; |
|
drm_modeset_acquire_init(&ctx, 0); |
|
retry: |
err = drm_modeset_lock_all_ctx(dev, &ctx); |
if (err < 0) { |
state = ERR_PTR(err); |
goto unlock; |
} |
|
state = drm_atomic_helper_duplicate_state(dev, &ctx); |
if (IS_ERR(state)) |
goto unlock; |
|
err = drm_atomic_helper_disable_all(dev, &ctx); |
if (err < 0) { |
drm_atomic_state_free(state); |
state = ERR_PTR(err); |
goto unlock; |
} |
|
unlock: |
if (PTR_ERR(state) == -EDEADLK) { |
drm_modeset_backoff(&ctx); |
goto retry; |
} |
|
drm_modeset_drop_locks(&ctx); |
drm_modeset_acquire_fini(&ctx); |
return state; |
} |
EXPORT_SYMBOL(drm_atomic_helper_suspend); |
|
/** |
* drm_atomic_helper_resume - subsystem-level resume helper |
* @dev: DRM device |
* @state: atomic state to resume to |
* |
* Calls drm_mode_config_reset() to synchronize hardware and software states, |
* grabs all modeset locks and commits the atomic state object. This can be |
* used in conjunction with the drm_atomic_helper_suspend() helper to |
* implement suspend/resume for drivers that support atomic mode-setting. |
* |
* Returns: |
* 0 on success or a negative error code on failure. |
* |
* See also: |
* drm_atomic_helper_suspend() |
*/ |
int drm_atomic_helper_resume(struct drm_device *dev, |
struct drm_atomic_state *state) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
int err; |
|
drm_mode_config_reset(dev); |
drm_modeset_lock_all(dev); |
state->acquire_ctx = config->acquire_ctx; |
err = drm_atomic_commit(state); |
drm_modeset_unlock_all(dev); |
|
return err; |
} |
EXPORT_SYMBOL(drm_atomic_helper_resume); |
|
/** |
* drm_atomic_helper_crtc_set_property - helper for crtc properties |
* @crtc: DRM crtc |
* @property: DRM property |
2047,6 → 2298,15 |
goto fail; |
drm_atomic_set_fb_for_plane(plane_state, fb); |
|
/* Make sure we don't accidentally do a full modeset. */ |
state->allow_modeset = false; |
if (!crtc_state->active) { |
DRM_DEBUG_ATOMIC("[CRTC:%d] disabled, rejecting legacy flip\n", |
crtc->base.id); |
ret = -EINVAL; |
goto fail; |
} |
|
ret = drm_atomic_async_commit(state); |
if (ret != 0) |
goto fail; |
2169,6 → 2429,12 |
* The simpler solution is to just reset the software state to everything off, |
* which is easiest to do by calling drm_mode_config_reset(). To facilitate this |
* the atomic helpers provide default reset implementations for all hooks. |
* |
* On the upside the precise state tracking of atomic simplifies system suspend |
* and resume a lot. For drivers using drm_mode_config_reset() a complete recipe |
* is implemented in drm_atomic_helper_suspend() and drm_atomic_helper_resume(). |
* For other drivers the building blocks are split out, see the documentation |
* for these functions. |
*/ |
|
/** |
2180,7 → 2446,7 |
*/ |
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) |
{ |
if (crtc->state && crtc->state->mode_blob) |
if (crtc->state) |
drm_property_unreference_blob(crtc->state->mode_blob); |
kfree(crtc->state); |
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); |
2248,7 → 2514,6 |
void __drm_atomic_helper_crtc_destroy_state(struct drm_crtc *crtc, |
struct drm_crtc_state *state) |
{ |
if (state->mode_blob) |
drm_property_unreference_blob(state->mode_blob); |
} |
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); |
2364,6 → 2629,28 |
EXPORT_SYMBOL(drm_atomic_helper_plane_destroy_state); |
|
/** |
* __drm_atomic_helper_connector_reset - reset state on connector |
* @connector: drm connector |
* @conn_state: connector state to assign |
* |
* Initializes the newly allocated @conn_state and assigns it to |
* #connector ->state, usually required when initializing the drivers |
* or when called from the ->reset hook. |
* |
* This is useful for drivers that subclass the connector state. |
*/ |
void |
__drm_atomic_helper_connector_reset(struct drm_connector *connector, |
struct drm_connector_state *conn_state) |
{ |
if (conn_state) |
conn_state->connector = connector; |
|
connector->state = conn_state; |
} |
EXPORT_SYMBOL(__drm_atomic_helper_connector_reset); |
|
/** |
* drm_atomic_helper_connector_reset - default ->reset hook for connectors |
* @connector: drm connector |
* |
2373,11 → 2660,11 |
*/ |
void drm_atomic_helper_connector_reset(struct drm_connector *connector) |
{ |
struct drm_connector_state *conn_state = |
kzalloc(sizeof(*conn_state), GFP_KERNEL); |
|
kfree(connector->state); |
connector->state = kzalloc(sizeof(*connector->state), GFP_KERNEL); |
|
if (connector->state) |
connector->state->connector = connector; |
__drm_atomic_helper_connector_reset(connector, conn_state); |
} |
EXPORT_SYMBOL(drm_atomic_helper_connector_reset); |
|
2426,7 → 2713,9 |
* @ctx: lock acquisition context |
* |
* Makes a copy of the current atomic state by looping over all objects and |
* duplicating their respective states. |
* duplicating their respective states. This is used for example by suspend/ |
* resume support code to save the state prior to suspend such that it can |
* be restored upon resume. |
* |
* Note that this treats atomic state as persistent between save and restore. |
* Drivers must make sure that this is possible and won't result in confusion |
2438,6 → 2727,9 |
* Returns: |
* A pointer to the copy of the atomic state object on success or an |
* ERR_PTR()-encoded error code on failure. |
* |
* See also: |
* drm_atomic_helper_suspend(), drm_atomic_helper_resume() |
*/ |
struct drm_atomic_state * |
drm_atomic_helper_duplicate_state(struct drm_device *dev, |