[PATCH 5/7] drm: convert crtc to properties/state
Rob Clark
robdclark at gmail.com
Wed Jul 23 12:38:18 PDT 2014
Break the mutable state of a crtc out into a separate structure
and use atomic properties mechanism to set crtc attributes. This
makes it easier to have some helpers for crtc->set_property()
and for checking for invalid params. The idea is that individual
drivers can wrap the state struct in their own struct which adds
driver specific parameters, for easy build-up of state across
multiple set_property() calls and for easy atomic commit or roll-
back.
Signed-off-by: Rob Clark <robdclark at gmail.com>
---
drivers/gpu/drm/armada/armada_crtc.c | 11 +-
drivers/gpu/drm/ast/ast_mode.c | 1 +
drivers/gpu/drm/cirrus/cirrus_mode.c | 1 +
drivers/gpu/drm/drm_atomic.c | 230 +++++++++-
drivers/gpu/drm/drm_crtc.c | 654 +++++++++++++++++++----------
drivers/gpu/drm/drm_fb_helper.c | 24 +-
drivers/gpu/drm/drm_irq.c | 8 +-
drivers/gpu/drm/exynos/exynos_drm_crtc.c | 7 +-
drivers/gpu/drm/gma500/cdv_intel_display.c | 1 +
drivers/gpu/drm/gma500/psb_intel_display.c | 1 +
drivers/gpu/drm/i915/intel_display.c | 3 +-
drivers/gpu/drm/mgag200/mgag200_mode.c | 1 +
drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c | 6 +-
drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c | 6 +-
drivers/gpu/drm/nouveau/dispnv04/crtc.c | 1 +
drivers/gpu/drm/nouveau/nv50_display.c | 1 +
drivers/gpu/drm/omapdrm/omap_crtc.c | 12 +-
drivers/gpu/drm/omapdrm/omap_drv.c | 2 +-
drivers/gpu/drm/qxl/qxl_display.c | 2 +
drivers/gpu/drm/radeon/radeon_display.c | 2 +
drivers/gpu/drm/rcar-du/rcar_du_crtc.c | 2 +
drivers/gpu/drm/shmobile/shmob_drm_crtc.c | 2 +
drivers/gpu/drm/tilcdc/tilcdc_crtc.c | 1 +
drivers/gpu/drm/udl/udl_modeset.c | 2 +
drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 1 +
drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c | 1 +
include/drm/drm_atomic.h | 29 ++
include/drm/drm_crtc.h | 103 ++++-
28 files changed, 864 insertions(+), 251 deletions(-)
diff --git a/drivers/gpu/drm/armada/armada_crtc.c b/drivers/gpu/drm/armada/armada_crtc.c
index 7d3c649..6237af4 100644
--- a/drivers/gpu/drm/armada/armada_crtc.c
+++ b/drivers/gpu/drm/armada/armada_crtc.c
@@ -9,6 +9,7 @@
#include <linux/clk.h>
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include "armada_crtc.h"
#include "armada_drm.h"
#include "armada_fb.h"
@@ -966,7 +967,12 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
{
struct armada_private *priv = crtc->dev->dev_private;
struct armada_crtc *dcrtc = drm_to_armada_crtc(crtc);
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
bool update_csc = false;
+ int ret = 0;
+
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
if (property == priv->csc_yuv_prop) {
dcrtc->csc_yuv_mode = val;
@@ -974,6 +980,9 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
} else if (property == priv->csc_rgb_prop) {
dcrtc->csc_rgb_mode = val;
update_csc = true;
+ } else {
+ ret = drm_crtc_set_property(crtc, cstate, property,
+ val, blob_data);
}
if (update_csc) {
@@ -984,7 +993,7 @@ armada_drm_crtc_set_property(struct drm_crtc *crtc,
writel_relaxed(val, dcrtc->base + LCD_SPU_IOPAD_CONTROL);
}
- return 0;
+ return ret;
}
static struct drm_crtc_funcs armada_crtc_funcs = {
diff --git a/drivers/gpu/drm/ast/ast_mode.c b/drivers/gpu/drm/ast/ast_mode.c
index 5389350..d9ec1bf 100644
--- a/drivers/gpu/drm/ast/ast_mode.c
+++ b/drivers/gpu/drm/ast/ast_mode.c
@@ -632,6 +632,7 @@ static const struct drm_crtc_funcs ast_crtc_funcs = {
.cursor_move = ast_cursor_move,
.reset = ast_crtc_reset,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.gamma_set = ast_crtc_gamma_set,
.destroy = ast_crtc_destroy,
};
diff --git a/drivers/gpu/drm/cirrus/cirrus_mode.c b/drivers/gpu/drm/cirrus/cirrus_mode.c
index e1c5c32..17c0bf7 100644
--- a/drivers/gpu/drm/cirrus/cirrus_mode.c
+++ b/drivers/gpu/drm/cirrus/cirrus_mode.c
@@ -366,6 +366,7 @@ static void cirrus_crtc_destroy(struct drm_crtc *crtc)
static const struct drm_crtc_funcs cirrus_crtc_funcs = {
.gamma_set = cirrus_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = cirrus_crtc_destroy,
};
diff --git a/drivers/gpu/drm/drm_atomic.c b/drivers/gpu/drm/drm_atomic.c
index 5a9916c..cc12114 100644
--- a/drivers/gpu/drm/drm_atomic.c
+++ b/drivers/gpu/drm/drm_atomic.c
@@ -49,11 +49,13 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
struct drm_atomic_state *state;
uint32_t acquire_flags = 0;
int nplanes = dev->mode_config.num_total_plane;
+ int ncrtcs = dev->mode_config.num_crtc;
int sz;
void *ptr;
sz = sizeof(*state);
sz += (sizeof(state->planes) + sizeof(state->pstates)) * nplanes;
+ sz += (sizeof(state->crtcs) + sizeof(state->cstates)) * ncrtcs;
ptr = kzalloc(sz, GFP_KERNEL);
@@ -78,6 +80,12 @@ struct drm_atomic_state *drm_atomic_begin(struct drm_device *dev,
state->pstates = ptr;
ptr = &state->pstates[nplanes];
+ state->crtcs = ptr;
+ ptr = &state->crtcs[ncrtcs];
+
+ state->cstates = ptr;
+ ptr = &state->cstates[ncrtcs];
+
return state;
}
EXPORT_SYMBOL(drm_atomic_begin);
@@ -96,7 +104,18 @@ int drm_atomic_set_event(struct drm_device *dev,
struct drm_atomic_state *state, struct drm_mode_object *obj,
struct drm_pending_vblank_event *event)
{
- return -EINVAL; /* for now */
+ switch (obj->type) {
+ case DRM_MODE_OBJECT_CRTC: {
+ struct drm_crtc_state *cstate =
+ drm_atomic_get_crtc_state(obj_to_crtc(obj), state);
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
+ cstate->event = event;
+ return 0;
+ }
+ default:
+ return -EINVAL;
+ }
}
EXPORT_SYMBOL(drm_atomic_set_event);
@@ -115,6 +134,7 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
{
struct drm_atomic_state *a = state;
int nplanes = dev->mode_config.num_total_plane;
+ int ncrtcs = dev->mode_config.num_crtc;
int i, ret = 0;
for (i = 0; i < nplanes; i++) {
@@ -124,6 +144,13 @@ int drm_atomic_check(struct drm_device *dev, struct drm_atomic_state *state)
break;
}
}
+ for (i = 0; i < ncrtcs; i++) {
+ if (a->crtcs[i]) {
+ ret = drm_atomic_check_crtc_state(a->crtcs[i], a->cstates[i]);
+ if (ret)
+ break;
+ }
+ }
a->acquire_ctx.frozen = true;
@@ -207,6 +234,7 @@ static void commit_locks(struct drm_atomic_state *a,
{
struct drm_device *dev = a->dev;
int nplanes = dev->mode_config.num_total_plane;
+ int ncrtcs = dev->mode_config.num_crtc;
int i;
for (i = 0; i < nplanes; i++) {
@@ -217,6 +245,14 @@ static void commit_locks(struct drm_atomic_state *a,
}
}
+ for (i = 0; i < ncrtcs; i++) {
+ struct drm_crtc *crtc = a->crtcs[i];
+ if (crtc) {
+ crtc->state->state = NULL;
+ drm_crtc_destroy_state(crtc, a->cstates[i]);
+ }
+ }
+
/* and properly release them (clear in_atomic, remove from list): */
drm_modeset_drop_locks(&a->acquire_ctx);
ww_acquire_fini(ww_ctx);
@@ -227,8 +263,18 @@ static int atomic_commit(struct drm_atomic_state *a,
struct ww_acquire_ctx *ww_ctx)
{
int nplanes = a->dev->mode_config.num_total_plane;
+ int ncrtcs = a->dev->mode_config.num_crtc;
int i, ret = 0;
+ for (i = 0; i < ncrtcs; i++) {
+ struct drm_crtc *crtc = a->crtcs[i];
+ if (crtc) {
+ ret = drm_atomic_commit_crtc_state(crtc, a->cstates[i]);
+ if (ret)
+ break;
+ }
+ }
+
for (i = 0; i < nplanes; i++) {
struct drm_plane *plane = a->planes[i];
if (plane) {
@@ -418,6 +464,7 @@ static int
commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
{
struct drm_atomic_state *a = pstate->state;
+ struct drm_crtc_state *cstate = NULL;
struct drm_framebuffer *old_fb = plane->fb;
struct drm_framebuffer *fb = pstate->fb;
bool enabled = pstate->crtc && fb;
@@ -439,8 +486,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
}
} else {
struct drm_crtc *crtc = pstate->crtc;
+ cstate = drm_atomic_get_crtc_state(crtc, pstate->state);
if (pstate->update_plane ||
(pstate->new_fb && !can_flip(plane, pstate))) {
+ WARN_ON(cstate->event);
ret = plane->funcs->update_plane(plane, crtc, pstate->fb,
pstate->crtc_x, pstate->crtc_y,
pstate->crtc_w, pstate->crtc_h,
@@ -457,7 +506,7 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
}
} else if (pstate->new_fb) {
- ret = crtc->funcs->page_flip(crtc, fb, NULL, a->flags);
+ ret = crtc->funcs->page_flip(crtc, fb, cstate->event, a->flags);
if (ret == 0) {
/*
* Warn if the driver hasn't properly updated the plane->fb
@@ -487,9 +536,10 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
* original code.
*/
swap_plane_state(plane, pstate->state);
+ if (cstate)
+ cstate->event = NULL;
}
-
if (fb)
drm_framebuffer_unreference(fb);
if (old_fb)
@@ -498,8 +548,182 @@ commit_plane_state(struct drm_plane *plane, struct drm_plane_state *pstate)
return ret;
}
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_atomic_state *state, struct drm_property *property,
+ uint64_t val, void *blob_data)
+{
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
+ return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+}
+EXPORT_SYMBOL(drm_atomic_crtc_set_property);
+
+static void init_crtc_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate, struct drm_atomic_state *state)
+{
+ /* snapshot current state: */
+ *cstate = *crtc->state;
+ cstate->state = state;
+
+ if (cstate->connector_ids) {
+ int sz = cstate->num_connector_ids * sizeof(cstate->connector_ids[0]);
+ cstate->connector_ids = kmemdup(cstate->connector_ids, sz, GFP_KERNEL);
+ }
+
+ /* this should never happen.. but make sure! */
+ WARN_ON(cstate->event);
+ cstate->event = NULL;
+}
+
+struct drm_crtc_state *
+drm_atomic_get_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+ struct drm_crtc_state *cstate;
+ int ret;
+
+ cstate = a->cstates[crtc->index];
+
+ if (!cstate) {
+ ret = drm_modeset_lock(&crtc->mutex, &a->acquire_ctx);
+ if (ret)
+ return ERR_PTR(ret);
+
+ cstate = drm_crtc_create_state(crtc);
+ if (!cstate)
+ return ERR_PTR(-ENOMEM);
+ init_crtc_state(crtc, cstate, a);
+ a->crtcs[crtc->index] = crtc;
+ a->cstates[crtc->index] = cstate;
+
+ /* we'll need it later, so make sure we have state
+ * for primary plane too:
+ */
+ drm_atomic_get_plane_state(crtc->primary, a);
+ }
+ return cstate;
+}
+EXPORT_SYMBOL(drm_atomic_get_crtc_state);
+
+static void
+swap_crtc_state(struct drm_crtc *crtc, struct drm_atomic_state *a)
+{
+ struct drm_crtc_state *cstate = a->cstates[crtc->index];
+ struct drm_device *dev = crtc->dev;
+ struct drm_pending_vblank_event *event = cstate->event;
+
+ if (event) {
+ /* hrm, need to sort out a better way to send events for
+ * other-than-pageflip.. but modeset is not async, so:
+ */
+ unsigned long flags;
+ spin_lock_irqsave(&dev->event_lock, flags);
+ drm_send_vblank_event(dev, crtc->index, event);
+ cstate->event = NULL;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ }
+
+ /* clear transient state (only valid during atomic update): */
+ cstate->set_config = false;
+ cstate->connectors_change = false;
+
+ swap(crtc->state, a->cstates[crtc->index]);
+ crtc->base.propvals = &crtc->state->propvals;
+}
+
+static struct drm_connector **get_connector_set(struct drm_device *dev,
+ uint32_t *connector_ids, uint32_t num_connector_ids)
+{
+ struct drm_connector **connector_set = NULL;
+ int i;
+
+ connector_set = kmalloc(num_connector_ids *
+ sizeof(struct drm_connector *),
+ GFP_KERNEL);
+ if (!connector_set)
+ return NULL;
+
+ for (i = 0; i < num_connector_ids; i++)
+ connector_set[i] = drm_connector_find(dev, connector_ids[i]);
+
+ return connector_set;
+}
+
+static int set_config(struct drm_crtc *crtc, struct drm_crtc_state *cstate)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_plane_state *pstate =
+ drm_atomic_get_plane_state(crtc->primary, cstate->state);
+ struct drm_framebuffer *fb = pstate->fb;
+ struct drm_connector **connector_set = get_connector_set(crtc->dev,
+ cstate->connector_ids, cstate->num_connector_ids);
+ struct drm_display_mode *mode = drm_crtc_get_mode(crtc, cstate);
+ struct drm_mode_set set = {
+ .crtc = crtc,
+ .x = pstate->src_x >> 16,
+ .y = pstate->src_y >> 16,
+ .mode = mode,
+ .num_connectors = cstate->num_connector_ids,
+ .connectors = connector_set,
+ .fb = fb,
+ };
+ int ret;
+
+ if (IS_ERR(mode)) {
+ ret = PTR_ERR(mode);
+ return ret;
+ }
+
+ if (fb)
+ drm_framebuffer_reference(fb);
+
+ ret = drm_mode_set_config_internal(&set);
+ if (!ret) {
+ swap_crtc_state(crtc, cstate->state);
+ pstate->new_fb = pstate->update_plane = false;
+ }
+
+ if (fb)
+ drm_framebuffer_unreference(fb);
+
+ kfree(connector_set);
+ if (mode)
+ drm_mode_destroy(dev, mode);
+ return ret;
+}
+
+static int
+commit_crtc_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate)
+{
+ struct drm_plane_state *pstate =
+ drm_atomic_get_plane_state(crtc->primary, cstate->state);
+ int ret = -EINVAL;
+
+ if (cstate->set_config)
+ return set_config(crtc, cstate);
+
+ if (!pstate->fb) {
+ /* disable */
+ struct drm_mode_set set = {
+ .crtc = crtc,
+ .fb = NULL,
+ };
+
+ ret = drm_mode_set_config_internal(&set);
+ if (!ret) {
+ swap_crtc_state(crtc, cstate->state);
+ }
+ }
+
+ return ret;
+}
+
const struct drm_atomic_funcs drm_atomic_funcs = {
.check_plane_state = drm_plane_check_state,
.commit_plane_state = commit_plane_state,
+
+ .check_crtc_state = drm_crtc_check_state,
+ .commit_crtc_state = commit_crtc_state,
};
EXPORT_SYMBOL(drm_atomic_funcs);
diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
index 026dcc7..d5ad7e1 100644
--- a/drivers/gpu/drm/drm_crtc.c
+++ b/drivers/gpu/drm/drm_crtc.c
@@ -670,10 +670,7 @@ EXPORT_SYMBOL(drm_framebuffer_cleanup);
void drm_framebuffer_remove(struct drm_framebuffer *fb)
{
struct drm_device *dev = fb->dev;
- struct drm_crtc *crtc;
struct drm_plane *plane;
- struct drm_mode_set set;
- int ret;
WARN_ON(!list_empty(&fb->filp_head));
@@ -693,7 +690,9 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
* in this manner.
*/
if (atomic_read(&fb->refcount.refcount) > 1) {
- void *state;
+ struct drm_mode_config *config = &dev->mode_config;
+ struct drm_atomic_state *state;
+ int ret;
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state)) {
@@ -701,24 +700,15 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
return;
}
- /* TODO once CRTC is converted to state/properties, we can push the
- * locking down into drm_atomic_commit(), since that is where
- * the actual changes take place..
- */
- drm_modeset_lock_all(dev);
- /* remove from any CRTC */
- list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
- if (crtc->primary->fb == fb) {
- /* should turn off the crtc */
- memset(&set, 0, sizeof(struct drm_mode_set));
- set.crtc = crtc;
- set.fb = NULL;
- ret = drm_mode_set_config_internal(&set);
- if (ret)
- DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc);
- }
- }
+retry:
+ ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+ if (ret)
+ goto out;
+ ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+ if (ret)
+ goto out;
+ /* remove from any plane */
list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
if (plane->fb == fb)
drm_plane_force_disable(plane, state);
@@ -730,9 +720,12 @@ void drm_framebuffer_remove(struct drm_framebuffer *fb)
else
dev->driver->atomic_commit(dev, state);
+out:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&state->acquire_ctx);
+ goto retry;
+ }
dev->driver->atomic_end(dev, state);
-
- drm_modeset_unlock_all(dev);
}
drm_framebuffer_unreference(fb);
@@ -763,9 +756,13 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
struct drm_mode_config *config = &dev->mode_config;
int ret;
+ /* this is now required: */
+ WARN_ON(!funcs->set_property);
+
crtc->dev = dev;
crtc->funcs = funcs;
- crtc->invert_dimensions = false;
+ crtc->state = drm_crtc_create_state(crtc);
+ crtc->state->invert_dimensions = false;
drm_modeset_lock_all(dev);
drm_modeset_lock_init(&crtc->mutex);
@@ -777,7 +774,7 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
goto out;
crtc->base.properties = &crtc->properties;
- crtc->base.propvals = &crtc->propvals;
+ crtc->base.propvals = &crtc->state->propvals;
list_add_tail(&crtc->head, &config->crtc_list);
config->num_crtc++;
@@ -785,9 +782,12 @@ int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
crtc->primary = primary;
crtc->cursor = cursor;
if (primary)
- primary->possible_crtcs = 1 << drm_crtc_index(crtc);
+ primary->possible_crtcs = 1 << crtc->index;
if (cursor)
- cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
+ cursor->possible_crtcs = 1 << crtc->index;
+
+ drm_object_attach_property(&crtc->base, config->prop_mode, 0);
+ drm_object_attach_property(&crtc->base, config->prop_connector_ids, 0);
out:
drm_modeset_unlock_all(dev);
@@ -816,31 +816,244 @@ void drm_crtc_cleanup(struct drm_crtc *crtc)
drm_mode_object_put(dev, &crtc->base);
list_del(&crtc->head);
dev->mode_config.num_crtc--;
+
+ drm_crtc_destroy_state(crtc, crtc->state);
}
EXPORT_SYMBOL(drm_crtc_cleanup);
-/**
- * drm_crtc_index - find the index of a registered CRTC
- * @crtc: CRTC to find index for
- *
- * Given a registered CRTC, return the index of that CRTC within a DRM
- * device's list of CRTCs.
- */
-unsigned int drm_crtc_index(struct drm_crtc *crtc)
+/* get display-mode from user-mode */
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate)
{
- unsigned int index = 0;
- struct drm_crtc *tmp;
+ struct drm_display_mode *mode = NULL;
+ if (cstate->mode_valid) {
+ struct drm_device *dev = crtc->dev;
+ int ret;
- list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) {
- if (tmp == crtc)
- return index;
+ mode = drm_mode_create(dev);
+ if (!mode)
+ return ERR_PTR(-ENOMEM);
- index++;
+ ret = drm_crtc_convert_umode(mode, &cstate->mode);
+ if (ret) {
+ DRM_DEBUG_KMS("Invalid mode\n");
+ drm_mode_destroy(dev, mode);
+ return ERR_PTR(ret);
+ }
+
+ drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
}
+ return mode;
+}
- BUG();
+static int connector_idx(struct drm_crtc_state *state,
+ uint32_t connector_id)
+{
+ int i;
+ for (i = 0; i < state->num_connector_ids; i++)
+ if (state->connector_ids[i] == connector_id)
+ return i;
+ return -1;
+}
+
+static int remove_connector(struct drm_crtc *ocrtc,
+ struct drm_crtc_state *ostate, struct drm_atomic_state *state,
+ int idx)
+{
+ struct drm_mode_config *config = &ocrtc->dev->mode_config;
+ uint32_t *new_connector_ids;
+ int a, b;
+
+ /* before deletion point: */
+ a = idx * sizeof(ostate->connector_ids[0]);
+
+ /* after deletion point: */
+ b = (ostate->num_connector_ids - 1 - idx) *
+ sizeof(ostate->connector_ids[0]);
+
+ new_connector_ids = kmalloc(a+b, GFP_KERNEL);
+ if (!new_connector_ids)
+ return -ENOMEM;
+
+ memcpy(new_connector_ids, ostate->connector_ids, a);
+ memcpy(&new_connector_ids[idx],
+ &ostate->connector_ids[idx + 1], b);
+
+ return drm_mode_crtc_set_obj_prop(ocrtc, state,
+ config->prop_connector_ids, a + b,
+ new_connector_ids);
+}
+
+static int check_connectors(struct drm_crtc *crtc,
+ struct drm_atomic_state *state, bool fix,
+ uint32_t *connector_ids, uint32_t num_connector_ids)
+{
+ struct drm_mode_config *config = &crtc->dev->mode_config;
+ struct drm_crtc *ocrtc; /* other connector */
+
+ list_for_each_entry(ocrtc, &config->crtc_list, head) {
+ struct drm_crtc_state *ostate; /* other state */
+ unsigned i;
+
+ if (ocrtc == crtc)
+ continue;
+
+ ostate = drm_atomic_get_crtc_state(crtc, state);
+ if (IS_ERR(ostate))
+ return PTR_ERR(ostate);
+
+ for (i = 0; i < num_connector_ids; i++) {
+ struct drm_connector *connector;
+ uint32_t cid = connector_ids[i];
+ int idx;
+
+retry:
+ idx = connector_idx(ostate, cid);
+ if (idx < 0)
+ continue;
+
+ if (fix) {
+ int ret = remove_connector(ocrtc,
+ ostate, state, idx);
+ if (ret)
+ return ret;
+ goto retry;
+ }
+
+ connector = drm_connector_find(crtc->dev, cid);
+ DRM_DEBUG_KMS("[CONNECTOR:%d:%s] already in use\n",
+ connector->base.id, connector->name);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+int drm_crtc_check_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ struct drm_plane *primary = crtc->primary;
+ struct drm_plane_state *pstate =
+ drm_atomic_get_plane_state(primary, state->state);
+ struct drm_framebuffer *fb = pstate->fb;
+ int hdisplay, vdisplay;
+ struct drm_display_mode *mode = drm_crtc_get_mode(crtc, state);
+ unsigned x, y;
+
+ if (IS_ERR(mode))
+ return PTR_ERR(mode);
+
+ /* disabling the crtc is allowed: */
+ if (!(fb && state->mode_valid))
+ return 0;
+
+ hdisplay = state->mode.hdisplay;
+ vdisplay = state->mode.vdisplay;
+
+ if (mode && drm_mode_is_stereo(mode)) {
+ struct drm_display_mode adjusted = *mode;
+
+ drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE);
+ hdisplay = adjusted.crtc_hdisplay;
+ vdisplay = adjusted.crtc_vdisplay;
+ }
+
+ if (state->invert_dimensions)
+ swap(hdisplay, vdisplay);
+
+ x = pstate->src_x >> 16;
+ y = pstate->src_y >> 16;
+
+ if (hdisplay > fb->width ||
+ vdisplay > fb->height ||
+ x > fb->width - hdisplay ||
+ y > fb->height - vdisplay) {
+ DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
+ fb->width, fb->height, hdisplay, vdisplay,
+ x, y, state->invert_dimensions ? " (inverted)" : "");
+ return -ENOSPC;
+ }
+
+ if (crtc->enabled && !state->set_config) {
+ if (primary->state->fb->pixel_format != fb->pixel_format) {
+ DRM_DEBUG_KMS("Page flip is not allowed to "
+ "change frame buffer format.\n");
+ return -EINVAL;
+ }
+ }
+
+ if (state->num_connector_ids == 0) {
+ DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
+ return -EINVAL;
+ }
+
+ if (state->connectors_change) {
+ int ret = check_connectors(crtc, state->state, false,
+ state->connector_ids, state->num_connector_ids);
+ if (ret)
+ return ret;
+ }
+
+ if (mode)
+ drm_mode_destroy(crtc->dev, mode);
+
+ return 0;
+}
+EXPORT_SYMBOL(drm_crtc_check_state);
+
+void drm_crtc_commit_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state)
+{
+ crtc->state = state;
+ crtc->base.propvals = &state->propvals;
+}
+EXPORT_SYMBOL(drm_crtc_commit_state);
+
+int drm_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t value, void *blob_data)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_config *config = &dev->mode_config;
+
+ /* grab primary plane state now, to ensure locks are held, etc. */
+ drm_atomic_get_plane_state(crtc->primary, state->state);
+
+ drm_object_property_set_value(&crtc->base,
+ &state->propvals, property, value, blob_data);
+
+ if (property == config->prop_mode) {
+ if (!blob_data) {
+ memset(&state->mode, 0, sizeof(state->mode));
+ state->mode_valid = false;
+ } else {
+ /* check size: */
+ if (value < sizeof(struct drm_mode_modeinfo))
+ return -EINVAL;
+ state->mode = *(struct drm_mode_modeinfo *)blob_data;
+ state->mode_valid = true;
+ }
+ state->set_config = true;
+ } else if (property == config->prop_connector_ids) {
+ /* if connector-id's changing, we need to have all the locks: */
+ struct drm_atomic_state *a = state->state;
+ int ret = drm_modeset_lock_all_crtcs(crtc->dev, &a->acquire_ctx);
+ if (ret)
+ return ret;
+ state->connectors_change = true;
+ state->num_connector_ids = value / sizeof(state->connector_ids[0]);
+ kfree(state->connector_ids);
+ state->connector_ids = blob_data;
+ state->set_config = true;
+ } else {
+ return -EINVAL;
+ }
+
+ return 0;
}
-EXPORT_SYMBOL(drm_crtc_index);
+EXPORT_SYMBOL(drm_crtc_set_property);
/*
* drm_mode_remove - remove and free a mode
@@ -1293,6 +1506,10 @@ int drm_plane_check_state(struct drm_plane *plane,
if (!fb)
return 0;
+ /* we'll need this later during commit: */
+ if (state->crtc)
+ drm_atomic_get_crtc_state(state->crtc, state->state);
+
fb_width = fb->width << 16;
fb_height = fb->height << 16;
@@ -1527,6 +1744,16 @@ static int drm_mode_create_standard_connector_properties(struct drm_device *dev)
return -ENOMEM;
dev->mode_config.path_property = prop;
+ prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_connector_ids = prop;
+
+ prop = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0);
+ if (!prop)
+ return -ENOMEM;
+ dev->mode_config.prop_mode = prop;
+
return 0;
}
@@ -1825,7 +2052,7 @@ static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out,
* Returns:
* Zero on success, errno on failure.
*/
-static int drm_crtc_convert_umode(struct drm_display_mode *out,
+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)
@@ -2072,8 +2299,8 @@ int drm_mode_getcrtc(struct drm_device *dev,
goto out;
}
- crtc_resp->x = crtc->x;
- crtc_resp->y = crtc->y;
+ crtc_resp->x = crtc->primary->state->src_x >> 16;
+ crtc_resp->y = crtc->primary->state->src_y >> 16;
crtc_resp->gamma_size = crtc->gamma_size;
if (crtc->primary->fb)
crtc_resp->fb_id = crtc->primary->fb->base.id;
@@ -2638,7 +2865,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
vdisplay = adjusted.crtc_vdisplay;
}
- if (crtc->invert_dimensions)
+ if (crtc->state->invert_dimensions)
swap(hdisplay, vdisplay);
if (hdisplay > fb->width ||
@@ -2647,7 +2874,7 @@ int drm_crtc_check_viewport(const struct drm_crtc *crtc,
y > fb->height - vdisplay) {
DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n",
fb->width, fb->height, hdisplay, vdisplay, x, y,
- crtc->invert_dimensions ? " (inverted)" : "");
+ crtc->state->invert_dimensions ? " (inverted)" : "");
return -ENOSPC;
}
@@ -2674,22 +2901,17 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
struct drm_mode_config *config = &dev->mode_config;
struct drm_mode_crtc *crtc_req = data;
struct drm_crtc *crtc;
- struct drm_connector **connector_set = NULL, *connector;
- struct drm_framebuffer *fb = NULL;
- struct drm_display_mode *mode = NULL;
- struct drm_mode_set set;
- uint32_t __user *set_connectors_ptr;
+ uint32_t fb_id = -1;
+ uint32_t *connector_ids = NULL;
+ struct drm_atomic_state *state = NULL;
int ret;
int i;
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
- /* For some reason crtc x/y offsets are signed internally. */
- if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX)
- return -ERANGE;
+ mutex_lock(&dev->mode_config.mutex);
- drm_modeset_lock_all(dev);
crtc = drm_crtc_find(dev, crtc_req->crtc_id);
if (!crtc) {
DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
@@ -2707,55 +2929,15 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
ret = -EINVAL;
goto out;
}
- fb = crtc->primary->fb;
- /* Make refcounting symmetric with the lookup path. */
- drm_framebuffer_reference(fb);
+ fb_id = crtc->primary->fb->base.id;
} else {
- fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
- if (!fb) {
- DRM_DEBUG_KMS("Unknown FB ID%d\n",
- crtc_req->fb_id);
- ret = -ENOENT;
- goto out;
- }
+ fb_id = crtc_req->fb_id;
}
-
- mode = drm_mode_create(dev);
- if (!mode) {
- ret = -ENOMEM;
- goto out;
- }
-
- ret = drm_crtc_convert_umode(mode, &crtc_req->mode);
- if (ret) {
- DRM_DEBUG_KMS("Invalid mode\n");
- goto out;
- }
-
- drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V);
-
- ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
- mode, fb);
- if (ret)
- goto out;
-
- }
-
- if (crtc_req->count_connectors == 0 && mode) {
- DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
- ret = -EINVAL;
- goto out;
- }
-
- if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
- DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
- crtc_req->count_connectors);
- ret = -EINVAL;
- goto out;
}
if (crtc_req->count_connectors > 0) {
- u32 out_id;
+ uint32_t __user *set_connectors_ptr =
+ (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
/* Avoid unbounded kernel memory allocation */
if (crtc_req->count_connectors > config->num_connector) {
@@ -2763,52 +2945,97 @@ int drm_mode_setcrtc(struct drm_device *dev, void *data,
goto out;
}
- connector_set = kmalloc(crtc_req->count_connectors *
- sizeof(struct drm_connector *),
+ connector_ids = kmalloc(crtc_req->count_connectors *
+ sizeof(connector_ids[0]),
GFP_KERNEL);
- if (!connector_set) {
+ if (!connector_ids) {
ret = -ENOMEM;
goto out;
}
for (i = 0; i < crtc_req->count_connectors; i++) {
- set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
+ u32 out_id;
+
if (get_user(out_id, &set_connectors_ptr[i])) {
ret = -EFAULT;
goto out;
}
-
- connector = drm_connector_find(dev, out_id);
- if (!connector) {
- DRM_DEBUG_KMS("Connector id %d unknown\n",
- out_id);
- ret = -ENOENT;
- goto out;
- }
- DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
- connector->base.id,
- connector->name);
-
- connector_set[i] = connector;
+ connector_ids[i] = out_id;
}
}
- set.crtc = crtc;
- set.x = crtc_req->x;
- set.y = crtc_req->y;
- set.mode = mode;
- set.connectors = connector_set;
- set.num_connectors = crtc_req->count_connectors;
- set.fb = fb;
- ret = drm_mode_set_config_internal(&set);
+ state = dev->driver->atomic_begin(dev, 0);
+ if (IS_ERR(state)) {
+ ret = PTR_ERR(state);
+ goto out;
+ }
-out:
- if (fb)
- drm_framebuffer_unreference(fb);
+retry:
+ ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+ if (ret)
+ goto out;
+ ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+ if (ret)
+ goto out;
- kfree(connector_set);
- drm_mode_destroy(dev, mode);
- drm_modeset_unlock_all(dev);
+ /* If connectors change, we need to check if we need to steal one
+ * from another CRTC.. setcrtc makes this implicit, but atomic
+ * treats it as an error so we need to handle here:
+ */
+ ret = check_connectors(crtc, state, true,
+ connector_ids, crtc_req->count_connectors);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_crtc_set_obj_prop(crtc, state,
+ config->prop_mode, sizeof(crtc_req->mode), &crtc_req->mode);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_crtc_set_obj_prop(crtc, state,
+ config->prop_connector_ids,
+ crtc_req->count_connectors * sizeof(connector_ids[0]),
+ connector_ids);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+ config->prop_crtc_id, crtc->base.id, NULL);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+ config->prop_fb_id, fb_id, NULL);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+ config->prop_src_x, crtc_req->x << 16, NULL);
+ if (ret)
+ goto out;
+
+ ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+ config->prop_src_y, crtc_req->y << 16, NULL);
+ if (ret)
+ goto out;
+
+ ret = dev->driver->atomic_check(dev, state);
+ if (ret)
+ goto out;
+
+ ret = dev->driver->atomic_commit(dev, state);
+
+out:
+ if (state) {
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&state->acquire_ctx);
+ goto retry;
+ }
+ dev->driver->atomic_end(dev, state);
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+ if (ret)
+ kfree(connector_ids);
return ret;
}
@@ -4319,9 +4546,6 @@ int drm_mode_crtc_set_obj_prop(struct drm_crtc *crtc,
if (crtc->funcs->set_property)
ret = crtc->funcs->set_property(crtc, state, property,
value, blob_data);
- if (!ret)
- drm_object_property_set_value(&crtc->base, &crtc->propvals,
- property, value, NULL);
return ret;
}
@@ -4494,11 +4718,11 @@ int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data,
if (!drm_core_check_feature(dev, DRIVER_MODESET))
return -EINVAL;
-retry:
state = dev->driver->atomic_begin(dev, 0);
if (IS_ERR(state))
return PTR_ERR(state);
+retry:
ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
if (ret)
goto out;
@@ -4519,9 +4743,11 @@ retry:
ret = dev->driver->atomic_commit(dev, state);
out:
- dev->driver->atomic_end(dev, state);
- if (ret == -EDEADLK)
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&state->acquire_ctx);
goto retry;
+ }
+ dev->driver->atomic_end(dev, state);
return ret;
}
@@ -4713,6 +4939,51 @@ out:
return ret;
}
+static struct drm_pending_vblank_event *create_vblank_event(
+ struct drm_device *dev, struct drm_file *file_priv, uint64_t user_data)
+{
+ struct drm_pending_vblank_event *e = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ if (file_priv->event_space < sizeof e->event) {
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ goto out;
+ }
+ file_priv->event_space -= sizeof e->event;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+
+ e = kzalloc(sizeof *e, GFP_KERNEL);
+ if (e == NULL) {
+ spin_lock_irqsave(&dev->event_lock, flags);
+ file_priv->event_space += sizeof e->event;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ goto out;
+ }
+
+ e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
+ e->event.base.length = sizeof e->event;
+ e->event.user_data = user_data;
+ e->base.event = &e->event.base;
+ e->base.file_priv = file_priv;
+ e->base.destroy =
+ (void (*) (struct drm_pending_event *)) kfree;
+
+out:
+ return e;
+}
+
+static void destroy_vblank_event(struct drm_device *dev,
+ struct drm_file *file_priv, struct drm_pending_vblank_event *e)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->event_lock, flags);
+ file_priv->event_space += sizeof e->event;
+ spin_unlock_irqrestore(&dev->event_lock, flags);
+ kfree(e);
+}
+
/**
* drm_mode_page_flip_ioctl - schedule an asynchronous fb update
* @dev: DRM device
@@ -4735,10 +5006,10 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
void *data, struct drm_file *file_priv)
{
struct drm_mode_crtc_page_flip *page_flip = data;
+ struct drm_mode_config *config = &dev->mode_config;
struct drm_crtc *crtc;
- struct drm_framebuffer *fb = NULL, *old_fb = NULL;
struct drm_pending_vblank_event *e = NULL;
- unsigned long flags;
+ struct drm_atomic_state *state;
int ret = -EINVAL;
if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
@@ -4752,92 +5023,43 @@ int drm_mode_page_flip_ioctl(struct drm_device *dev,
if (!crtc)
return -ENOENT;
- drm_modeset_lock(&crtc->mutex, NULL);
- if (crtc->primary->fb == NULL) {
- /* The framebuffer is currently unbound, presumably
- * due to a hotplug event, that userspace has not
- * yet discovered.
- */
- ret = -EBUSY;
- goto out;
- }
-
- if (crtc->funcs->page_flip == NULL)
- goto out;
-
- fb = drm_framebuffer_lookup(dev, page_flip->fb_id);
- if (!fb) {
- ret = -ENOENT;
- goto out;
- }
-
- ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb);
- if (ret)
- goto out;
-
- if (crtc->primary->fb->pixel_format != fb->pixel_format) {
- DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n");
- ret = -EINVAL;
- goto out;
- }
+ state = dev->driver->atomic_begin(dev,
+ page_flip->flags | DRM_MODE_ATOMIC_NONBLOCK);
+ if (IS_ERR(state))
+ return PTR_ERR(state);
+retry:
if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- ret = -ENOMEM;
- spin_lock_irqsave(&dev->event_lock, flags);
- if (file_priv->event_space < sizeof e->event) {
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ e = create_vblank_event(dev, file_priv, page_flip->user_data);
+ if (!e) {
+ ret = -ENOMEM;
goto out;
}
- file_priv->event_space -= sizeof e->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
-
- e = kzalloc(sizeof *e, GFP_KERNEL);
- if (e == NULL) {
- spin_lock_irqsave(&dev->event_lock, flags);
- file_priv->event_space += sizeof e->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
+ ret = dev->driver->atomic_set_event(dev, state, &crtc->base, e);
+ if (ret) {
goto out;
}
-
- e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
- e->event.base.length = sizeof e->event;
- e->event.user_data = page_flip->user_data;
- e->base.event = &e->event.base;
- e->base.file_priv = file_priv;
- e->base.destroy =
- (void (*) (struct drm_pending_event *)) kfree;
}
- old_fb = crtc->primary->fb;
- ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags);
- if (ret) {
- if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
- spin_lock_irqsave(&dev->event_lock, flags);
- file_priv->event_space += sizeof e->event;
- spin_unlock_irqrestore(&dev->event_lock, flags);
- kfree(e);
- }
- /* Keep the old fb, don't unref it. */
- old_fb = NULL;
- } else {
- /*
- * Warn if the driver hasn't properly updated the crtc->fb
- * field to reflect that the new framebuffer is now used.
- * Failing to do so will screw with the reference counting
- * on framebuffers.
- */
- WARN_ON(crtc->primary->fb != fb);
- /* Unref only the old framebuffer. */
- fb = NULL;
- }
+ ret = drm_mode_plane_set_obj_prop(crtc->primary, state,
+ config->prop_fb_id, page_flip->fb_id, NULL);
+ if (ret)
+ goto out;
-out:
- if (fb)
- drm_framebuffer_unreference(fb);
- if (old_fb)
- drm_framebuffer_unreference(old_fb);
- drm_modeset_unlock(&crtc->mutex);
+ ret = dev->driver->atomic_check(dev, state);
+ if (ret)
+ goto out;
+
+ ret = dev->driver->atomic_commit(dev, state);
+out:
+ if (ret && e)
+ destroy_vblank_event(dev, file_priv, e);
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&state->acquire_ctx);
+ goto retry;
+ }
+ dev->driver->atomic_end(dev, state);
return ret;
}
diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
index 0dc0780..300649b 100644
--- a/drivers/gpu/drm/drm_fb_helper.c
+++ b/drivers/gpu/drm/drm_fb_helper.c
@@ -36,6 +36,7 @@
#include <linux/module.h>
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_fb_helper.h>
#include <drm/drm_crtc_helper.h>
@@ -340,10 +341,11 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
bool lockless)
{
struct drm_device *dev = fb_helper->dev;
+ struct drm_mode_config *config = &dev->mode_config;
struct drm_plane *plane;
bool error = false;
- void *state;
- int i;
+ struct drm_atomic_state *state;
+ int ret, i;
state = dev->driver->atomic_begin(dev, lockless ?
DRM_MODE_ATOMIC_NOLOCK : 0);
@@ -352,6 +354,14 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
return true;
}
+retry:
+ ret = drm_modeset_lock(&config->connection_mutex, &state->acquire_ctx);
+ if (ret)
+ goto out;
+ ret = drm_modeset_lock_all_crtcs(dev, &state->acquire_ctx);
+ if (ret)
+ goto out;
+
list_for_each_entry(plane, &dev->mode_config.plane_list, head)
if (plane->type != DRM_PLANE_TYPE_PRIMARY)
drm_plane_force_disable(plane, state);
@@ -362,8 +372,15 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
else
dev->driver->atomic_commit(dev, state);
+out:
+ if (ret == -EDEADLK) {
+ drm_modeset_backoff(&state->acquire_ctx);
+ goto retry;
+ }
dev->driver->atomic_end(dev, state);
+ drm_modeset_lock_all(dev);
+
for (i = 0; i < fb_helper->crtc_count; i++) {
struct drm_mode_set *mode_set = &fb_helper->crtc_info[i].mode_set;
struct drm_crtc *crtc = mode_set->crtc;
@@ -379,6 +396,9 @@ static bool restore_fbdev_mode(struct drm_fb_helper *fb_helper,
if (ret)
error = true;
}
+
+ drm_modeset_unlock_all(dev);
+
return error;
}
/**
diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
index 0de123a..200276d 100644
--- a/drivers/gpu/drm/drm_irq.c
+++ b/drivers/gpu/drm/drm_irq.c
@@ -957,7 +957,7 @@ EXPORT_SYMBOL(drm_vblank_get);
*/
int drm_crtc_vblank_get(struct drm_crtc *crtc)
{
- return drm_vblank_get(crtc->dev, drm_crtc_index(crtc));
+ return drm_vblank_get(crtc->dev, crtc->index);
}
EXPORT_SYMBOL(drm_crtc_vblank_get);
@@ -994,7 +994,7 @@ EXPORT_SYMBOL(drm_vblank_put);
*/
void drm_crtc_vblank_put(struct drm_crtc *crtc)
{
- drm_vblank_put(crtc->dev, drm_crtc_index(crtc));
+ drm_vblank_put(crtc->dev, crtc->index);
}
EXPORT_SYMBOL(drm_crtc_vblank_put);
@@ -1058,7 +1058,7 @@ EXPORT_SYMBOL(drm_vblank_off);
*/
void drm_crtc_vblank_off(struct drm_crtc *crtc)
{
- drm_vblank_off(crtc->dev, drm_crtc_index(crtc));
+ drm_vblank_off(crtc->dev, crtc->index);
}
EXPORT_SYMBOL(drm_crtc_vblank_off);
@@ -1099,7 +1099,7 @@ EXPORT_SYMBOL(drm_vblank_on);
*/
void drm_crtc_vblank_on(struct drm_crtc *crtc)
{
- drm_vblank_on(crtc->dev, drm_crtc_index(crtc));
+ drm_vblank_on(crtc->dev, crtc->index);
}
EXPORT_SYMBOL(drm_crtc_vblank_on);
diff --git a/drivers/gpu/drm/exynos/exynos_drm_crtc.c b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
index 4cb016b..0b7af3f 100644
--- a/drivers/gpu/drm/exynos/exynos_drm_crtc.c
+++ b/drivers/gpu/drm/exynos/exynos_drm_crtc.c
@@ -14,6 +14,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include "exynos_drm_crtc.h"
#include "exynos_drm_drv.h"
@@ -289,6 +290,10 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
struct drm_device *dev = crtc->dev;
struct exynos_drm_private *dev_priv = dev->dev_private;
struct exynos_drm_crtc *exynos_crtc = to_exynos_crtc(crtc);
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
if (property == dev_priv->crtc_mode_property) {
enum exynos_crtc_mode mode = val;
@@ -313,7 +318,7 @@ static int exynos_drm_crtc_set_property(struct drm_crtc *crtc,
return 0;
}
- return -EINVAL;
+ return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
static struct drm_crtc_funcs exynos_crtc_funcs = {
diff --git a/drivers/gpu/drm/gma500/cdv_intel_display.c b/drivers/gpu/drm/gma500/cdv_intel_display.c
index 6672732..5b6eee9 100644
--- a/drivers/gpu/drm/gma500/cdv_intel_display.c
+++ b/drivers/gpu/drm/gma500/cdv_intel_display.c
@@ -989,6 +989,7 @@ const struct drm_crtc_funcs cdv_intel_crtc_funcs = {
.cursor_move = gma_crtc_cursor_move,
.gamma_set = gma_crtc_gamma_set,
.set_config = gma_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = gma_crtc_destroy,
};
diff --git a/drivers/gpu/drm/gma500/psb_intel_display.c b/drivers/gpu/drm/gma500/psb_intel_display.c
index 87b50ba..79b5692 100644
--- a/drivers/gpu/drm/gma500/psb_intel_display.c
+++ b/drivers/gpu/drm/gma500/psb_intel_display.c
@@ -444,6 +444,7 @@ const struct drm_crtc_funcs psb_intel_crtc_funcs = {
.cursor_move = gma_crtc_cursor_move,
.gamma_set = gma_crtc_gamma_set,
.set_config = gma_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = gma_crtc_destroy,
};
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 4fa9dd1..830b8f2 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -11308,6 +11308,7 @@ out_config:
static const struct drm_crtc_funcs intel_crtc_funcs = {
.gamma_set = intel_crtc_gamma_set,
.set_config = intel_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = intel_crtc_destroy,
.page_flip = intel_crtc_page_flip,
};
@@ -11765,7 +11766,7 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
- WARN_ON(drm_crtc_index(&intel_crtc->base) != intel_crtc->pipe);
+ WARN_ON(intel_crtc->base.index != intel_crtc->pipe);
return;
fail:
diff --git a/drivers/gpu/drm/mgag200/mgag200_mode.c b/drivers/gpu/drm/mgag200/mgag200_mode.c
index 45f04de..730bed9 100644
--- a/drivers/gpu/drm/mgag200/mgag200_mode.c
+++ b/drivers/gpu/drm/mgag200/mgag200_mode.c
@@ -1296,6 +1296,7 @@ static const struct drm_crtc_funcs mga_crtc_funcs = {
.cursor_move = mga_crtc_cursor_move,
.gamma_set = mga_crtc_gamma_set,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = mga_crtc_destroy,
};
diff --git a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
index 60d7e73..cb68082 100644
--- a/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp4/mdp4_crtc.c
@@ -469,8 +469,10 @@ static int mdp4_crtc_set_property(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
{
- // XXX
- return -EINVAL;
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
+ return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
#define CURSOR_WIDTH 64
diff --git a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
index deb6647..54d20a5 100644
--- a/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
+++ b/drivers/gpu/drm/msm/mdp/mdp5/mdp5_crtc.c
@@ -387,8 +387,10 @@ static int mdp5_crtc_set_property(struct drm_crtc *crtc,
struct drm_atomic_state *state, struct drm_property *property,
uint64_t val, void *blob_data)
{
- // XXX
- return -EINVAL;
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
+ return drm_crtc_set_property(crtc, cstate, property, val, blob_data);
}
static const struct drm_crtc_funcs mdp5_crtc_funcs = {
diff --git a/drivers/gpu/drm/nouveau/dispnv04/crtc.c b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
index 41be342..9e24632 100644
--- a/drivers/gpu/drm/nouveau/dispnv04/crtc.c
+++ b/drivers/gpu/drm/nouveau/dispnv04/crtc.c
@@ -1086,6 +1086,7 @@ static const struct drm_crtc_funcs nv04_crtc_funcs = {
.cursor_move = nv04_crtc_cursor_move,
.gamma_set = nv_crtc_gamma_set,
.set_config = nouveau_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.page_flip = nouveau_crtc_page_flip,
.destroy = nv_crtc_destroy,
};
diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
index 4c534b7..65d477d 100644
--- a/drivers/gpu/drm/nouveau/nv50_display.c
+++ b/drivers/gpu/drm/nouveau/nv50_display.c
@@ -1331,6 +1331,7 @@ static const struct drm_crtc_funcs nv50_crtc_func = {
.cursor_move = nv50_crtc_cursor_move,
.gamma_set = nv50_crtc_gamma_set,
.set_config = nouveau_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = nv50_crtc_destroy,
.page_flip = nouveau_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/omapdrm/omap_crtc.c b/drivers/gpu/drm/omapdrm/omap_crtc.c
index a75934d..772687b 100644
--- a/drivers/gpu/drm/omapdrm/omap_crtc.c
+++ b/drivers/gpu/drm/omapdrm/omap_crtc.c
@@ -387,14 +387,22 @@ static int omap_crtc_set_property(struct drm_crtc *crtc,
{
struct omap_crtc *omap_crtc = to_omap_crtc(crtc);
struct omap_drm_private *priv = crtc->dev->dev_private;
+ struct drm_crtc_state *cstate = drm_atomic_get_crtc_state(crtc, state);
+ int ret;
+
+ if (IS_ERR(cstate))
+ return PTR_ERR(cstate);
if (property == priv->rotation_prop) {
- crtc->invert_dimensions =
+ cstate->invert_dimensions =
!!(val & ((1LL << DRM_ROTATE_90) | (1LL << DRM_ROTATE_270)));
}
- return omap_plane_set_property(omap_crtc->plane, state,
+ ret = omap_plane_set_property(omap_crtc->plane, state,
property, val, blob_data);
+ if (ret)
+ ret = drm_crtc_set_property(crtc, cstate, property, val, blob_data);
+ return ret;
}
static const struct drm_crtc_funcs omap_crtc_funcs = {
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c
index 9630a32..4b4bc42 100644
--- a/drivers/gpu/drm/omapdrm/omap_drv.c
+++ b/drivers/gpu/drm/omapdrm/omap_drv.c
@@ -579,7 +579,7 @@ static void dev_lastclose(struct drm_device *dev)
*/
for (i = 0; i < priv->num_crtcs; i++) {
drm_object_property_set_value(&priv->crtcs[i]->base,
- &priv->crtcs[i]->propvals,
+ &priv->crtcs[i]->state->propvals,
priv->rotation_prop, 0, NULL);
}
diff --git a/drivers/gpu/drm/qxl/qxl_display.c b/drivers/gpu/drm/qxl/qxl_display.c
index 3dcc70c..118e498 100644
--- a/drivers/gpu/drm/qxl/qxl_display.c
+++ b/drivers/gpu/drm/qxl/qxl_display.c
@@ -29,6 +29,7 @@
#include "qxl_drv.h"
#include "qxl_object.h"
#include "drm_crtc_helper.h"
+#include "drm_atomic.h"
static bool qxl_head_enabled(struct qxl_head *head)
{
@@ -373,6 +374,7 @@ static const struct drm_crtc_funcs qxl_crtc_funcs = {
.cursor_set2 = qxl_crtc_cursor_set2,
.cursor_move = qxl_crtc_cursor_move,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = qxl_crtc_destroy,
};
diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
index 13896ed..c89c20d 100644
--- a/drivers/gpu/drm/radeon/radeon_display.c
+++ b/drivers/gpu/drm/radeon/radeon_display.c
@@ -32,6 +32,7 @@
#include <linux/pm_runtime.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_edid.h>
#include <linux/gcd.h>
@@ -622,6 +623,7 @@ static const struct drm_crtc_funcs radeon_crtc_funcs = {
.cursor_move = radeon_crtc_cursor_move,
.gamma_set = radeon_crtc_gamma_set,
.set_config = radeon_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = radeon_crtc_destroy,
.page_flip = radeon_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
index 299267d..f5a3d55 100644
--- a/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
+++ b/drivers/gpu/drm/rcar-du/rcar_du_crtc.c
@@ -17,6 +17,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
@@ -527,6 +528,7 @@ static int rcar_du_crtc_page_flip(struct drm_crtc *crtc,
static const struct drm_crtc_funcs crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.page_flip = rcar_du_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
index 78b2a86..4022025 100644
--- a/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
+++ b/drivers/gpu/drm/shmobile/shmob_drm_crtc.c
@@ -17,6 +17,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include <drm/drm_fb_cma_helper.h>
#include <drm/drm_gem_cma_helper.h>
@@ -506,6 +507,7 @@ static int shmob_drm_crtc_page_flip(struct drm_crtc *crtc,
static const struct drm_crtc_funcs crtc_funcs = {
.destroy = drm_crtc_cleanup,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.page_flip = shmob_drm_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
index d642d4a0..95ffae7 100644
--- a/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
+++ b/drivers/gpu/drm/tilcdc/tilcdc_crtc.c
@@ -411,6 +411,7 @@ static int tilcdc_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
static const struct drm_crtc_funcs tilcdc_crtc_funcs = {
.destroy = tilcdc_crtc_destroy,
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.page_flip = tilcdc_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/udl/udl_modeset.c b/drivers/gpu/drm/udl/udl_modeset.c
index dc145d3..b597a25 100644
--- a/drivers/gpu/drm/udl/udl_modeset.c
+++ b/drivers/gpu/drm/udl/udl_modeset.c
@@ -14,6 +14,7 @@
#include <drm/drmP.h>
#include <drm/drm_crtc.h>
#include <drm/drm_crtc_helper.h>
+#include <drm/drm_atomic.h>
#include "udl_drv.h"
/*
@@ -403,6 +404,7 @@ static struct drm_crtc_helper_funcs udl_helper_funcs = {
static const struct drm_crtc_funcs udl_crtc_funcs = {
.set_config = drm_crtc_helper_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.destroy = udl_crtc_destroy,
.page_flip = udl_crtc_page_flip,
};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
index 15e185a..6ed37de 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -300,6 +300,7 @@ static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
.cursor_move = vmw_du_crtc_cursor_move,
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_ldu_crtc_destroy,
+ .set_property = drm_atomic_crtc_set_property,
.set_config = vmw_ldu_crtc_set_config,
};
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
index b295463..20fda38 100644
--- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c
@@ -397,6 +397,7 @@ static struct drm_crtc_funcs vmw_screen_object_crtc_funcs = {
.gamma_set = vmw_du_crtc_gamma_set,
.destroy = vmw_sou_crtc_destroy,
.set_config = vmw_sou_crtc_set_config,
+ .set_property = drm_atomic_crtc_set_property,
.page_flip = vmw_du_page_flip,
};
diff --git a/include/drm/drm_atomic.h b/include/drm/drm_atomic.h
index 78e93ec..7946b7f 100644
--- a/include/drm/drm_atomic.h
+++ b/include/drm/drm_atomic.h
@@ -70,6 +70,9 @@
struct drm_atomic_funcs {
int (*check_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
int (*commit_plane_state)(struct drm_plane *plane, struct drm_plane_state *pstate);
+
+ int (*check_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
+ int (*commit_crtc_state)(struct drm_crtc *crtc, struct drm_crtc_state *cstate);
};
const extern struct drm_atomic_funcs drm_atomic_funcs;
@@ -109,6 +112,30 @@ drm_atomic_commit_plane_state(struct drm_plane *plane,
return funcs->commit_plane_state(plane, pstate);
}
+int drm_atomic_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_atomic_state *state, struct drm_property *property,
+ uint64_t val, void *blob_data);
+struct drm_crtc_state *drm_atomic_get_crtc_state(struct drm_crtc *crtc,
+ struct drm_atomic_state *state);
+
+static inline int
+drm_atomic_check_crtc_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate)
+{
+ const struct drm_atomic_funcs *funcs =
+ crtc->dev->driver->atomic_funcs;
+ return funcs->check_crtc_state(crtc, cstate);
+}
+
+static inline int
+drm_atomic_commit_crtc_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate)
+{
+ const struct drm_atomic_funcs *funcs =
+ crtc->dev->driver->atomic_funcs;
+ return funcs->commit_crtc_state(crtc, cstate);
+}
+
/**
* struct drm_atomic_state - the state object used by atomic helpers
*/
@@ -118,6 +145,8 @@ struct drm_atomic_state {
uint32_t flags;
struct drm_plane **planes;
struct drm_plane_state **pstates;
+ struct drm_crtc **crtcs;
+ struct drm_crtc_state **cstates;
bool committed;
bool checked; /* just for debugging */
diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
index b10eaac..084fce0 100644
--- a/include/drm/drm_crtc.h
+++ b/include/drm/drm_crtc.h
@@ -287,6 +287,10 @@ struct drm_crtc_funcs {
struct drm_pending_vblank_event *event,
uint32_t flags);
+ struct drm_crtc_state *(*create_state)(struct drm_crtc *crtc);
+ void (*destroy_state)(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate);
+
int (*set_property)(struct drm_crtc *crtc,
struct drm_atomic_state *state,
struct drm_property *property, uint64_t val,
@@ -294,21 +298,52 @@ struct drm_crtc_funcs {
};
/**
+ * drm_crtc_state - mutable crtc state
+ * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
+ * invert the width/height of the crtc. This is used if the driver
+ * is performing 90 or 270 degree rotated scanout
+ * @mode_valid: a valid mode has been set
+ * @set_config: needs modeset (crtc->set_config())
+ * @connectors_change: the connector-ids array has changed
+ * @num_connector_ids: the number of connector-ids
+ * @connector_ids: array of connector ids
+ * @mode: current mode timings
+ * @event: pending pageflip event
+ * @propvals: property values
+ * @state: current global/toplevel state object (for atomic) while an
+ * update is in progress, NULL otherwise.
+ */
+struct drm_crtc_state {
+ bool invert_dimensions : 1;
+ bool mode_valid : 1;
+
+ /* transient state, only valid during atomic operation: */
+ bool set_config : 1;
+ bool connectors_change : 1;
+
+ uint8_t num_connector_ids;
+ uint32_t *connector_ids;
+ struct drm_mode_modeinfo mode;
+
+ struct drm_pending_vblank_event *event;
+
+ struct drm_object_property_values propvals;
+
+ struct drm_atomic_state *state;
+};
+
+/**
* drm_crtc - central CRTC control structure
* @dev: parent DRM device
* @head: list management
+ * @index: CRTC number, 0..n
* @mutex: per-CRTC locking
* @base: base KMS object for ID tracking etc.
* @primary: primary plane for this CRTC
* @cursor: cursor plane for this CRTC
+ * @state: the mutable state
* @enabled: is this CRTC enabled?
- * @mode: current mode timings
* @hwmode: mode timings as programmed to hw regs
- * @invert_dimensions: for purposes of error checking crtc vs fb sizes,
- * invert the width/height of the crtc. This is used if the driver
- * is performing 90 or 270 degree rotated scanout
- * @x: x position on screen
- * @y: y position on screen
* @funcs: CRTC control functions
* @gamma_size: size of gamma ramp
* @gamma_store: gamma ramp values
@@ -325,6 +360,8 @@ struct drm_crtc {
struct drm_device *dev;
struct list_head head;
+ int index;
+
/**
* crtc mutex
*
@@ -344,23 +381,19 @@ struct drm_crtc {
int cursor_x;
int cursor_y;
+ struct drm_crtc_state *state;
+
/* Temporary tracking of the old fb while a modeset is ongoing. Used
* by drm_mode_set_config_internal to implement correct refcounting. */
struct drm_framebuffer *old_fb;
bool enabled;
- /* Requested mode from modesetting. */
- struct drm_display_mode mode;
-
/* Programmed mode in hw, after adjustments for encoders,
* crtc, panel scaling etc. Needed for timestamping etc.
*/
struct drm_display_mode hwmode;
- bool invert_dimensions;
-
- int x, y;
const struct drm_crtc_funcs *funcs;
/* CRTC gamma size for reporting to userspace */
@@ -374,9 +407,15 @@ struct drm_crtc {
void *helper_private;
struct drm_object_properties properties;
- struct drm_object_property_values propvals;
-};
+ /* These are (temporary) duplicate information from what is in the
+ * drm_crtc_state struct.. keeping duplicate copy here makes the
+ * switch to atomic far less intrusive. Once all the drivers and
+ * the crtc/fb helpers are updated, then we can remove these:
+ */
+ int x, y;
+ struct drm_display_mode mode;
+};
/**
* drm_connector_funcs - control connectors on a given device
@@ -888,6 +927,8 @@ struct drm_mode_config {
struct drm_property *prop_crtc_h;
struct drm_property *prop_fb_id;
struct drm_property *prop_crtc_id;
+ struct drm_property *prop_connector_ids;
+ struct drm_property *prop_mode;
struct drm_property *edid_property;
struct drm_property *dpms_property;
struct drm_property *path_property;
@@ -949,7 +990,8 @@ extern int drm_crtc_init(struct drm_device *dev,
struct drm_crtc *crtc,
const struct drm_crtc_funcs *funcs);
extern void drm_crtc_cleanup(struct drm_crtc *crtc);
-extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
+struct drm_display_mode *drm_crtc_get_mode(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate);
/**
* drm_crtc_mask - find the mask of a registered CRTC
@@ -960,9 +1002,18 @@ extern unsigned int drm_crtc_index(struct drm_crtc *crtc);
*/
static inline uint32_t drm_crtc_mask(struct drm_crtc *crtc)
{
- return 1 << drm_crtc_index(crtc);
+ return 1 << crtc->index;
}
+extern int drm_crtc_check_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state);
+extern void drm_crtc_commit_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *state);
+extern int drm_crtc_set_property(struct drm_crtc *crtc,
+ struct drm_crtc_state *state,
+ struct drm_property *property,
+ uint64_t value, void *blob_data);
+
extern void drm_connector_ida_init(void);
extern void drm_connector_ida_destroy(void);
extern int drm_connector_init(struct drm_device *dev,
@@ -1040,6 +1091,7 @@ extern void drm_fb_release(struct drm_file *file_priv);
extern int drm_mode_group_init_legacy_group(struct drm_device *dev, struct drm_mode_group *group);
extern void drm_mode_group_destroy(struct drm_mode_group *group);
extern void drm_reinit_primary_mode_group(struct drm_device *dev);
+extern int drm_crtc_convert_umode(struct drm_display_mode *out, const struct drm_mode_modeinfo *in);
extern bool drm_probe_ddc(struct i2c_adapter *adapter);
extern struct edid *drm_get_edid(struct drm_connector *connector,
struct i2c_adapter *adapter);
@@ -1268,6 +1320,25 @@ drm_property_blob_find(struct drm_device *dev, uint32_t id)
return mo ? obj_to_blob(mo) : NULL;
}
+static inline struct drm_crtc_state *
+drm_crtc_create_state(struct drm_crtc *crtc)
+{
+ if (crtc->funcs->create_state)
+ return crtc->funcs->create_state(crtc);
+ return kzalloc(sizeof(struct drm_crtc_state), GFP_KERNEL);
+}
+
+static inline void
+drm_crtc_destroy_state(struct drm_crtc *crtc,
+ struct drm_crtc_state *cstate)
+{
+ kfree(cstate->connector_ids);
+ if (crtc->funcs->destroy_state)
+ crtc->funcs->destroy_state(crtc, cstate);
+ else
+ kfree(cstate);
+}
+
static inline struct drm_plane_state *
drm_plane_create_state(struct drm_plane *plane)
{
--
1.9.3
More information about the dri-devel
mailing list