67,7 → 67,8 |
struct drm_crtc_state *crtc_state; |
|
if (plane->state->crtc) { |
crtc_state = state->crtc_states[drm_crtc_index(plane->state->crtc)]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, |
plane->state->crtc); |
|
if (WARN_ON(!crtc_state)) |
return; |
76,8 → 77,8 |
} |
|
if (plane_state->crtc) { |
crtc_state = |
state->crtc_states[drm_crtc_index(plane_state->crtc)]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, |
plane_state->crtc); |
|
if (WARN_ON(!crtc_state)) |
return; |
86,111 → 87,186 |
} |
} |
|
static bool |
check_pending_encoder_assignment(struct drm_atomic_state *state, |
struct drm_encoder *new_encoder) |
static int handle_conflicting_encoders(struct drm_atomic_state *state, |
bool disable_conflicting_encoders) |
{ |
struct drm_connector_state *conn_state; |
struct drm_connector *connector; |
struct drm_connector_state *conn_state; |
int i; |
struct drm_encoder *encoder; |
unsigned encoder_mask = 0; |
int i, ret; |
|
/* |
* First loop, find all newly assigned encoders from the connectors |
* part of the state. If the same encoder is assigned to multiple |
* connectors bail out. |
*/ |
for_each_connector_in_state(state, connector, conn_state, i) { |
if (conn_state->best_encoder != new_encoder) |
const struct drm_connector_helper_funcs *funcs = connector->helper_private; |
struct drm_encoder *new_encoder; |
|
if (!conn_state->crtc) |
continue; |
|
/* encoder already assigned and we're trying to re-steal it! */ |
if (connector->state->best_encoder != conn_state->best_encoder) |
return false; |
if (funcs->atomic_best_encoder) |
new_encoder = funcs->atomic_best_encoder(connector, conn_state); |
else |
new_encoder = funcs->best_encoder(connector); |
|
if (new_encoder) { |
if (encoder_mask & (1 << drm_encoder_index(new_encoder))) { |
DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] on [CONNECTOR:%d:%s] already assigned\n", |
new_encoder->base.id, new_encoder->name, |
connector->base.id, connector->name); |
|
return -EINVAL; |
} |
|
return true; |
encoder_mask |= 1 << drm_encoder_index(new_encoder); |
} |
} |
|
static struct drm_crtc * |
get_current_crtc_for_encoder(struct drm_device *dev, |
if (!encoder_mask) |
return 0; |
|
/* |
* Second loop, iterate over all connectors not part of the state. |
* |
* If a conflicting encoder is found and disable_conflicting_encoders |
* is not set, an error is returned. Userspace can provide a solution |
* through the atomic ioctl. |
* |
* If the flag is set conflicting connectors are removed from the crtc |
* and the crtc is disabled if no encoder is left. This preserves |
* compatibility with the legacy set_config behavior. |
*/ |
drm_for_each_connector(connector, state->dev) { |
struct drm_crtc_state *crtc_state; |
|
if (drm_atomic_get_existing_connector_state(state, connector)) |
continue; |
|
encoder = connector->state->best_encoder; |
if (!encoder || !(encoder_mask & (1 << drm_encoder_index(encoder)))) |
continue; |
|
if (!disable_conflicting_encoders) { |
DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s] by [CONNECTOR:%d:%s]\n", |
encoder->base.id, encoder->name, |
connector->state->crtc->base.id, |
connector->state->crtc->name, |
connector->base.id, connector->name); |
return -EINVAL; |
} |
|
conn_state = drm_atomic_get_connector_state(state, connector); |
if (IS_ERR(conn_state)) |
return PTR_ERR(conn_state); |
|
DRM_DEBUG_ATOMIC("[ENCODER:%d:%s] in use on [CRTC:%d:%s], disabling [CONNECTOR:%d:%s]\n", |
encoder->base.id, encoder->name, |
conn_state->crtc->base.id, conn_state->crtc->name, |
connector->base.id, connector->name); |
|
crtc_state = drm_atomic_get_existing_crtc_state(state, conn_state->crtc); |
|
ret = drm_atomic_set_crtc_for_connector(conn_state, NULL); |
if (ret) |
return ret; |
|
if (!crtc_state->connector_mask) { |
ret = drm_atomic_set_mode_prop_for_crtc(crtc_state, |
NULL); |
if (ret < 0) |
return ret; |
|
crtc_state->active = false; |
} |
} |
|
return 0; |
} |
|
static void |
set_best_encoder(struct drm_atomic_state *state, |
struct drm_connector_state *conn_state, |
struct drm_encoder *encoder) |
{ |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_connector *connector; |
struct drm_crtc_state *crtc_state; |
struct drm_crtc *crtc; |
|
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); |
if (conn_state->best_encoder) { |
/* Unset the encoder_mask in the old crtc state. */ |
crtc = conn_state->connector->state->crtc; |
|
drm_for_each_connector(connector, dev) { |
if (connector->state->best_encoder != encoder) |
continue; |
/* A NULL crtc is an error here because we should have |
* duplicated a NULL best_encoder when crtc was NULL. |
* As an exception restoring duplicated atomic state |
* during resume is allowed, so don't warn when |
* best_encoder is equal to encoder we intend to set. |
*/ |
WARN_ON(!crtc && encoder != conn_state->best_encoder); |
if (crtc) { |
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); |
|
return connector->state->crtc; |
crtc_state->encoder_mask &= |
~(1 << drm_encoder_index(conn_state->best_encoder)); |
} |
} |
|
return NULL; |
if (encoder) { |
crtc = conn_state->crtc; |
WARN_ON(!crtc); |
if (crtc) { |
crtc_state = drm_atomic_get_existing_crtc_state(state, crtc); |
|
crtc_state->encoder_mask |= |
1 << drm_encoder_index(encoder); |
} |
} |
|
static int |
conn_state->best_encoder = encoder; |
} |
|
static void |
steal_encoder(struct drm_atomic_state *state, |
struct drm_encoder *encoder, |
struct drm_crtc *encoder_crtc) |
struct drm_encoder *encoder) |
{ |
struct drm_mode_config *config = &state->dev->mode_config; |
struct drm_crtc_state *crtc_state; |
struct drm_connector *connector; |
struct drm_connector_state *connector_state; |
int ret; |
int i; |
|
/* |
* We can only steal an encoder coming from a connector, which means we |
* must already hold the connection_mutex. |
*/ |
WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); |
for_each_connector_in_state(state, connector, connector_state, i) { |
struct drm_crtc *encoder_crtc; |
|
if (connector_state->best_encoder != encoder) |
continue; |
|
encoder_crtc = connector->state->crtc; |
|
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->name); |
|
crtc_state = drm_atomic_get_crtc_state(state, encoder_crtc); |
if (IS_ERR(crtc_state)) |
return PTR_ERR(crtc_state); |
set_best_encoder(state, connector_state, NULL); |
|
crtc_state = drm_atomic_get_existing_crtc_state(state, encoder_crtc); |
crtc_state->connectors_changed = true; |
|
list_for_each_entry(connector, &config->connector_list, head) { |
if (connector->state->best_encoder != encoder) |
continue; |
|
DRM_DEBUG_ATOMIC("Stealing encoder from [CONNECTOR:%d:%s]\n", |
connector->base.id, |
connector->name); |
|
connector_state = drm_atomic_get_connector_state(state, |
connector); |
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; |
return; |
} |
|
return 0; |
} |
|
static int |
update_connector_routing(struct drm_atomic_state *state, int conn_idx) |
update_connector_routing(struct drm_atomic_state *state, |
struct drm_connector *connector, |
struct drm_connector_state *connector_state) |
{ |
const struct drm_connector_helper_funcs *funcs; |
struct drm_encoder *new_encoder; |
struct drm_crtc *encoder_crtc; |
struct drm_connector *connector; |
struct drm_connector_state *connector_state; |
struct drm_crtc_state *crtc_state; |
int idx, ret; |
|
connector = state->connectors[conn_idx]; |
connector_state = state->connector_states[conn_idx]; |
|
if (!connector) |
return 0; |
|
DRM_DEBUG_ATOMIC("Updating routing for [CONNECTOR:%d:%s]\n", |
connector->base.id, |
connector->name); |
197,16 → 273,12 |
|
if (connector->state->crtc != connector_state->crtc) { |
if (connector->state->crtc) { |
idx = drm_crtc_index(connector->state->crtc); |
|
crtc_state = state->crtc_states[idx]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, connector->state->crtc); |
crtc_state->connectors_changed = true; |
} |
|
if (connector_state->crtc) { |
idx = drm_crtc_index(connector_state->crtc); |
|
crtc_state = state->crtc_states[idx]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); |
crtc_state->connectors_changed = true; |
} |
} |
216,7 → 288,7 |
connector->base.id, |
connector->name); |
|
connector_state->best_encoder = NULL; |
set_best_encoder(state, connector_state, NULL); |
|
return 0; |
} |
245,6 → 317,8 |
} |
|
if (new_encoder == connector_state->best_encoder) { |
set_best_encoder(state, connector_state, new_encoder); |
|
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] keeps [ENCODER:%d:%s], now on [CRTC:%d:%s]\n", |
connector->base.id, |
connector->name, |
256,33 → 330,11 |
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; |
} |
steal_encoder(state, new_encoder); |
|
encoder_crtc = get_current_crtc_for_encoder(state->dev, |
new_encoder); |
set_best_encoder(state, connector_state, new_encoder); |
|
if (encoder_crtc) { |
ret = steal_encoder(state, new_encoder, encoder_crtc); |
if (ret) { |
DRM_DEBUG_ATOMIC("Encoder stealing failed for [CONNECTOR:%d:%s]\n", |
connector->base.id, |
connector->name); |
return ret; |
} |
} |
|
if (WARN_ON(!connector_state->crtc)) |
return -EINVAL; |
|
connector_state->best_encoder = new_encoder; |
idx = drm_crtc_index(connector_state->crtc); |
|
crtc_state = state->crtc_states[idx]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, connector_state->crtc); |
crtc_state->connectors_changed = true; |
|
DRM_DEBUG_ATOMIC("[CONNECTOR:%d:%s] using [ENCODER:%d:%s] on [CRTC:%d:%s]\n", |
323,8 → 375,8 |
if (!conn_state->crtc || !conn_state->best_encoder) |
continue; |
|
crtc_state = |
state->crtc_states[drm_crtc_index(conn_state->crtc)]; |
crtc_state = drm_atomic_get_existing_crtc_state(state, |
conn_state->crtc); |
|
/* |
* Each encoder has at most one connector (since we always steal |
445,6 → 497,10 |
} |
} |
|
ret = handle_conflicting_encoders(state, state->legacy_set_config); |
if (ret) |
return ret; |
|
for_each_connector_in_state(state, connector, connector_state, i) { |
/* |
* This only sets crtc->mode_changed for routing changes, |
451,7 → 507,8 |
* drivers must set crtc->mode_changed themselves when connector |
* properties need to be updated. |
*/ |
ret = update_connector_routing(state, i); |
ret = update_connector_routing(state, connector, |
connector_state); |
if (ret) |
return ret; |
} |
617,7 → 674,6 |
for_each_connector_in_state(old_state, connector, old_conn_state, i) { |
const struct drm_encoder_helper_funcs *funcs; |
struct drm_encoder *encoder; |
struct drm_crtc_state *old_crtc_state; |
|
/* Shut down everything that's in the changeset and currently |
* still on. So need to check the old, saved state. */ |
624,7 → 680,8 |
if (!old_conn_state->crtc) |
continue; |
|
old_crtc_state = old_state->crtc_states[drm_crtc_index(old_conn_state->crtc)]; |
old_crtc_state = drm_atomic_get_existing_crtc_state(old_state, |
old_conn_state->crtc); |
|
if (!old_crtc_state->active || |
!drm_atomic_crtc_needs_modeset(old_conn_state->crtc->state)) |
1719,7 → 1776,7 |
struct drm_crtc_state *crtc_state; |
struct drm_connector *connector; |
struct drm_connector_state *conn_state; |
int ret, i, j; |
int ret, i; |
|
ret = drm_modeset_lock(&dev->mode_config.connection_mutex, |
state->acquire_ctx); |
1726,21 → 1783,11 |
if (ret) |
return ret; |
|
/* First grab all affected connector/crtc states. */ |
for (i = 0; i < set->num_connectors; i++) { |
conn_state = drm_atomic_get_connector_state(state, |
set->connectors[i]); |
if (IS_ERR(conn_state)) |
return PTR_ERR(conn_state); |
} |
|
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
ret = drm_atomic_add_affected_connectors(state, crtc); |
/* First disable all connectors on the target crtc. */ |
ret = drm_atomic_add_affected_connectors(state, set->crtc); |
if (ret) |
return ret; |
} |
|
/* Then recompute connector->crtc links and crtc enabling state. */ |
for_each_connector_in_state(state, connector, conn_state, i) { |
if (conn_state->crtc == set->crtc) { |
ret = drm_atomic_set_crtc_for_connector(conn_state, |
1748,17 → 1795,20 |
if (ret) |
return ret; |
} |
} |
|
for (j = 0; j < set->num_connectors; j++) { |
if (set->connectors[j] == connector) { |
/* Then set all connectors from set->connectors on the target crtc */ |
for (i = 0; i < set->num_connectors; i++) { |
conn_state = drm_atomic_get_connector_state(state, |
set->connectors[i]); |
if (IS_ERR(conn_state)) |
return PTR_ERR(conn_state); |
|
ret = drm_atomic_set_crtc_for_connector(conn_state, |
set->crtc); |
if (ret) |
return ret; |
break; |
} |
} |
} |
|
for_each_crtc_in_state(state, crtc, crtc_state, i) { |
/* Don't update ->enable for the CRTC in the set_config request, |
1800,6 → 1850,7 |
if (!state) |
return -ENOMEM; |
|
state->legacy_set_config = true; |
state->acquire_ctx = drm_modeset_legacy_acquire_ctx(crtc); |
retry: |
ret = __drm_atomic_helper_set_config(set, state); |
2446,8 → 2497,12 |
*/ |
void drm_atomic_helper_crtc_reset(struct drm_crtc *crtc) |
{ |
if (crtc->state) |
if (crtc->state) { |
drm_property_unreference_blob(crtc->state->mode_blob); |
drm_property_unreference_blob(crtc->state->degamma_lut); |
drm_property_unreference_blob(crtc->state->ctm); |
drm_property_unreference_blob(crtc->state->gamma_lut); |
} |
kfree(crtc->state); |
crtc->state = kzalloc(sizeof(*crtc->state), GFP_KERNEL); |
|
2471,10 → 2526,17 |
|
if (state->mode_blob) |
drm_property_reference_blob(state->mode_blob); |
if (state->degamma_lut) |
drm_property_reference_blob(state->degamma_lut); |
if (state->ctm) |
drm_property_reference_blob(state->ctm); |
if (state->gamma_lut) |
drm_property_reference_blob(state->gamma_lut); |
state->mode_changed = false; |
state->active_changed = false; |
state->planes_changed = false; |
state->connectors_changed = false; |
state->color_mgmt_changed = false; |
state->event = NULL; |
} |
EXPORT_SYMBOL(__drm_atomic_helper_crtc_duplicate_state); |
2515,6 → 2577,9 |
struct drm_crtc_state *state) |
{ |
drm_property_unreference_blob(state->mode_blob); |
drm_property_unreference_blob(state->degamma_lut); |
drm_property_unreference_blob(state->ctm); |
drm_property_unreference_blob(state->gamma_lut); |
} |
EXPORT_SYMBOL(__drm_atomic_helper_crtc_destroy_state); |
|
2549,9 → 2614,11 |
kfree(plane->state); |
plane->state = kzalloc(sizeof(*plane->state), GFP_KERNEL); |
|
if (plane->state) |
if (plane->state) { |
plane->state->plane = plane; |
plane->state->rotation = BIT(DRM_ROTATE_0); |
} |
} |
EXPORT_SYMBOL(drm_atomic_helper_plane_reset); |
|
/** |
2826,3 → 2893,98 |
kfree(state); |
} |
EXPORT_SYMBOL(drm_atomic_helper_connector_destroy_state); |
|
/** |
* drm_atomic_helper_legacy_gamma_set - set the legacy gamma correction table |
* @crtc: CRTC object |
* @red: red correction table |
* @green: green correction table |
* @blue: green correction table |
* @start: |
* @size: size of the tables |
* |
* Implements support for legacy gamma correction table for drivers |
* that support color management through the DEGAMMA_LUT/GAMMA_LUT |
* properties. |
*/ |
void drm_atomic_helper_legacy_gamma_set(struct drm_crtc *crtc, |
u16 *red, u16 *green, u16 *blue, |
uint32_t start, uint32_t size) |
{ |
struct drm_device *dev = crtc->dev; |
struct drm_mode_config *config = &dev->mode_config; |
struct drm_atomic_state *state; |
struct drm_crtc_state *crtc_state; |
struct drm_property_blob *blob = NULL; |
struct drm_color_lut *blob_data; |
int i, ret = 0; |
|
state = drm_atomic_state_alloc(crtc->dev); |
if (!state) |
return; |
|
blob = drm_property_create_blob(dev, |
sizeof(struct drm_color_lut) * size, |
NULL); |
if (IS_ERR(blob)) { |
ret = PTR_ERR(blob); |
blob = NULL; |
goto fail; |
} |
|
/* Prepare GAMMA_LUT with the legacy values. */ |
blob_data = (struct drm_color_lut *) blob->data; |
for (i = 0; i < size; i++) { |
blob_data[i].red = red[i]; |
blob_data[i].green = green[i]; |
blob_data[i].blue = blue[i]; |
} |
|
state->acquire_ctx = crtc->dev->mode_config.acquire_ctx; |
retry: |
crtc_state = drm_atomic_get_crtc_state(state, crtc); |
if (IS_ERR(crtc_state)) { |
ret = PTR_ERR(crtc_state); |
goto fail; |
} |
|
/* Reset DEGAMMA_LUT and CTM properties. */ |
ret = drm_atomic_crtc_set_property(crtc, crtc_state, |
config->degamma_lut_property, 0); |
if (ret) |
goto fail; |
|
ret = drm_atomic_crtc_set_property(crtc, crtc_state, |
config->ctm_property, 0); |
if (ret) |
goto fail; |
|
ret = drm_atomic_crtc_set_property(crtc, crtc_state, |
config->gamma_lut_property, blob->base.id); |
if (ret) |
goto fail; |
|
ret = drm_atomic_commit(state); |
if (ret) |
goto fail; |
|
/* Driver takes ownership of state on successful commit. */ |
|
drm_property_unreference_blob(blob); |
|
return; |
fail: |
if (ret == -EDEADLK) |
goto backoff; |
|
drm_atomic_state_free(state); |
drm_property_unreference_blob(blob); |
|
return; |
backoff: |
drm_atomic_state_clear(state); |
drm_atomic_legacy_backoff(state); |
|
goto retry; |
} |
EXPORT_SYMBOL(drm_atomic_helper_legacy_gamma_set); |