[RFC][PATCH 10/10] WIP drm/i915: "Atomic" modeset test implementation
ville.syrjala at linux.intel.com
ville.syrjala at linux.intel.com
Wed Jun 27 03:24:12 PDT 2012
From: Ville Syrjälä <ville.syrjala at linux.intel.com>
Second attempt at the atomic modeset implementation. This versions does
things in a way that's closer to the drm_crtc_helper way. Not exactly
pretty, but there's too much code depending on that design to change it.
Signed-off-by: Ville Syrjälä <ville.syrjala at linux.intel.com>
---
drivers/gpu/drm/i915/Makefile | 1 +
drivers/gpu/drm/i915/intel_atomic.c | 1101 ++++++++++++++++++++++++++++++++++
drivers/gpu/drm/i915/intel_display.c | 7 +
3 files changed, 1109 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpu/drm/i915/intel_atomic.c
diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
index 2e9268d..f97e7a4 100644
--- a/drivers/gpu/drm/i915/Makefile
+++ b/drivers/gpu/drm/i915/Makefile
@@ -15,6 +15,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o \
i915_gem_tiling.o \
i915_sysfs.o \
i915_trace_points.o \
+ intel_atomic.o \
intel_display.o \
intel_crt.o \
intel_lvds.o \
diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c
new file mode 100644
index 0000000..173721a
--- /dev/null
+++ b/drivers/gpu/drm/i915/intel_atomic.c
@@ -0,0 +1,1101 @@
+/*
+ */
+
+#include <drm/drmP.h>
+#include <drm/drm_crtc.h>
+
+#include "intel_drv.h"
+
+static struct drm_property *prop_src_x;
+static struct drm_property *prop_src_y;
+static struct drm_property *prop_src_w;
+static struct drm_property *prop_src_h;
+static struct drm_property *prop_crtc_x;
+static struct drm_property *prop_crtc_y;
+static struct drm_property *prop_crtc_w;
+static struct drm_property *prop_crtc_h;
+static struct drm_property *prop_fb_id;
+static struct drm_property *prop_crtc_id;
+static struct drm_property *prop_mode_id;
+static struct drm_property *prop_connector_ids;
+
+struct intel_plane_state {
+ struct drm_plane *plane;
+ struct intel_plane_coords coords;
+ bool dirty;
+};
+
+struct intel_crtc_state {
+ struct drm_crtc *crtc;
+ struct drm_framebuffer *old_fb;
+ bool mode_dirty;
+ bool fb_dirty;
+ bool active_dirty;
+ unsigned long connectors_bitmask;
+ unsigned long encoders_bitmask;
+};
+
+struct intel_atomic_state {
+ struct intel_plane_state *plane;
+ struct intel_crtc_state *crtc;
+ bool dirty;
+ bool restore_hw;
+ struct drm_plane *saved_planes;
+ struct drm_crtc *saved_crtcs;
+ struct drm_connector *saved_connectors;
+ struct drm_encoder *saved_encoders;
+};
+
+static void update_connectors_bitmask(struct intel_crtc_state *st)
+{
+ struct drm_device *dev = st->crtc->dev;
+ struct drm_connector *connector;
+ unsigned int i;
+
+ st->connectors_bitmask = 0;
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder && connector->encoder->crtc == st->crtc)
+ __set_bit(i, &st->connectors_bitmask);
+
+ i++;
+ }
+}
+
+static void update_encoders_bitmask(struct intel_crtc_state *st)
+{
+ struct drm_device *dev = st->crtc->dev;
+ struct drm_encoder *encoder;
+ unsigned int i;
+
+ st->encoders_bitmask = 0;
+
+ i = 0;
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == st->crtc)
+ __set_bit(i, &st->encoders_bitmask);
+
+ i++;
+ }
+}
+
+static int process_connectors(struct intel_crtc_state *s, const uint32_t *ids, int count_ids)
+{
+ struct drm_crtc *crtc = s->crtc;
+ struct drm_device *dev = crtc->dev;
+ struct drm_connector *connectors[count_ids];
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ int i;
+
+ for (i = 0; i < count_ids; i++) {
+ struct drm_encoder *encoder;
+ const struct drm_connector_helper_funcs *connector_funcs;
+ struct drm_mode_object *obj;
+ int j;
+
+ /* don't accept duplicates */
+ for (j = i + 1; j < count_ids; j++)
+ if (ids[i] == ids[j])
+ return -EINVAL;
+
+ obj = drm_mode_object_find(dev, ids[i], DRM_MODE_OBJECT_CONNECTOR);
+ if (!obj)
+ return -ENOENT;
+
+ connector = obj_to_connector(obj);
+ connector_funcs = connector->helper_private;
+
+ encoder = connector_funcs->best_encoder(connector);
+
+ if (!drm_encoder_crtc_ok(encoder, crtc))
+ return -EINVAL;
+
+ connectors[i] = connector;
+ }
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ const struct drm_connector_helper_funcs *connector_funcs =
+ connector->helper_private;
+
+ for (i = 0; i < count_ids; i++) {
+ if (connector == connectors[i])
+ break;
+ }
+
+ /* this connector isn't in the set */
+ if (i == count_ids) {
+ /* remove the link to the encoder if this crtc was driving it previously */
+ if (connector->encoder && connector->encoder->crtc == crtc) {
+ s->mode_dirty = true;
+ connector->encoder = NULL;
+ }
+ continue;
+ }
+
+ encoder = connector_funcs->best_encoder(connector);
+
+ if (encoder != connector->encoder) {
+ s->mode_dirty = true;
+ connector->encoder = encoder;
+ }
+
+ if (crtc != encoder->crtc) {
+ s->mode_dirty = true;
+ encoder->crtc = crtc;
+ }
+ }
+
+ /* prune dangling encoder->crtc links pointing to this crtc */
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc && !drm_helper_encoder_in_use(encoder))
+ encoder->crtc = NULL;
+ }
+
+ update_connectors_bitmask(s);
+ update_encoders_bitmask(s);
+
+ return 0;
+}
+
+static size_t intel_atomic_state_size(const struct drm_device *dev)
+{
+ struct intel_atomic_state *state;
+ unsigned int num_connector = dev->mode_config.num_connector;
+ unsigned int num_encoder = dev->mode_config.num_encoder;
+ unsigned int num_crtc = dev->mode_config.num_crtc;
+ unsigned int num_plane = dev->mode_config.num_plane;
+
+ return sizeof *state +
+ num_crtc * sizeof state->crtc[0] +
+ num_plane * sizeof state->plane[0] +
+ num_connector * sizeof state->saved_connectors[0] +
+ num_encoder * sizeof state->saved_encoders[0] +
+ num_crtc * sizeof state->saved_crtcs[0] +
+ num_plane * sizeof state->saved_planes[0];
+}
+
+static void *intel_atomic_begin(struct drm_device *dev, uint32_t flags)
+{
+ struct intel_atomic_state *state;
+ struct drm_plane *plane;
+ struct drm_crtc *crtc;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ unsigned int num_connector = dev->mode_config.num_connector;
+ unsigned int num_encoder = dev->mode_config.num_encoder;
+ unsigned int num_crtc = dev->mode_config.num_crtc;
+ unsigned int num_plane = dev->mode_config.num_plane;
+ int i;
+
+ state = kzalloc(intel_atomic_state_size(dev), GFP_KERNEL);
+ if (!state)
+ return ERR_PTR(-ENOMEM);
+
+ state->crtc = (struct intel_crtc_state *)(state + 1);
+ state->plane = (struct intel_plane_state *)(state->crtc + num_crtc);
+
+ state->saved_connectors = (struct drm_connector *)(state->plane + num_plane);
+ state->saved_encoders = (struct drm_encoder *)(state->saved_connectors + num_connector);
+ state->saved_crtcs = (struct drm_crtc *)(state->saved_encoders + num_encoder);
+ state->saved_planes = (struct drm_plane *)(state->saved_crtcs + num_crtc);
+
+ i = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct intel_crtc_state *s = &state->crtc[i++];
+
+ s->crtc = crtc;
+ s->old_fb = crtc->fb;
+
+ update_connectors_bitmask(s);
+ update_encoders_bitmask(s);
+ }
+
+ i = 0;
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ struct intel_plane_state *s = &state->plane[i++];
+
+ s->plane = plane;
+ }
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ state->saved_connectors[i++] = *connector;
+ i = 0;
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ state->saved_encoders[i++] = *encoder;
+ i = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ state->saved_crtcs[i++] = *crtc;
+ i = 0;
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+ state->saved_planes[i++] = *plane;
+
+ return state;
+}
+
+static int plane_set(struct intel_atomic_state *s,
+ struct intel_plane_state *state,
+ struct drm_property *prop,
+ uint64_t value)
+{
+ struct drm_plane *plane = state->plane;
+ struct drm_mode_object *obj;
+
+ if (prop == prop_src_x) {
+ if (plane->src_x == value)
+ return 0;
+ plane->src_x = value;
+ } else if (prop == prop_src_y) {
+ if (plane->src_y == value)
+ return 0;
+ plane->src_y = value;
+ } else if (prop == prop_src_w) {
+ if (plane->src_w == value)
+ return 0;
+ plane->src_w = value;
+ } else if (prop == prop_src_h) {
+ if (plane->src_h == value)
+ return 0;
+ plane->src_h = value;
+ } else if (prop == prop_crtc_x) {
+ if (plane->crtc_x == value)
+ return 0;
+ plane->crtc_x = value;
+ } else if (prop == prop_crtc_y) {
+ if (plane->crtc_y == value)
+ return 0;
+ plane->crtc_y = value;
+ } else if (prop == prop_crtc_w) {
+ if (plane->crtc_w == value)
+ return 0;
+ plane->crtc_w = value;
+ } else if (prop == prop_crtc_h) {
+ if (plane->crtc_h == value)
+ return 0;
+ plane->crtc_h = value;
+ } else if (prop == prop_crtc_id) {
+ struct drm_crtc *crtc = NULL;
+
+ if (plane->crtc) {
+ if (value == plane->crtc->base.id)
+ return 0;
+ } else {
+ if (value == 0)
+ return 0;
+ }
+
+ if (value) {
+ obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_CRTC);
+ if (!obj) {
+ printk("Unknown CRTC ID %llu\n", value);
+ return -ENOENT;
+ }
+ crtc = obj_to_crtc(obj);
+ }
+
+ plane->crtc = crtc;
+ } else if (prop == prop_fb_id) {
+ struct drm_framebuffer *fb = NULL;
+
+ if (plane->fb) {
+ if (value == plane->fb->base.id)
+ return 0;
+ } else {
+ if (value == 0)
+ return 0;
+ }
+
+ if (value) {
+ obj = drm_mode_object_find(plane->dev, value, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ printk("Unknown framebuffer ID %llu\n", value);
+ return -ENOENT;
+ }
+ fb = obj_to_fb(obj);
+ }
+
+ plane->fb = fb;
+ } else
+ return -ENOENT;
+
+ state->dirty = true;
+ s->dirty = true;
+
+ return 0;
+}
+
+static int crtc_set(struct intel_atomic_state *s,
+ struct intel_crtc_state *state,
+ struct drm_property *prop,
+ uint64_t value, void *blob_data)
+{
+ struct drm_crtc *crtc = state->crtc;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_mode_object *obj;
+
+ if (prop == prop_src_x) {
+ if (crtc->x == value)
+ return 0;
+ crtc->x = value;
+ if (crtc_funcs->mode_set_base)
+ state->fb_dirty = true;
+ else
+ state->mode_dirty = true;
+ } else if (prop == prop_src_y) {
+ if (crtc->y == value)
+ return 0;
+ crtc->y = value;
+ if (crtc_funcs->mode_set_base)
+ state->fb_dirty = true;
+ else
+ state->mode_dirty = true;
+ } else if (prop == prop_mode_id) {
+ struct drm_display_mode *mode = NULL;
+
+ if (!crtc->enabled) {
+ if (value == 0)
+ return 0;
+ }
+
+ if (value) {
+ obj = drm_mode_object_find(crtc->dev, value, DRM_MODE_OBJECT_MODE);
+ if (!obj) {
+ printk("Unknown mode ID %llu\n", value);
+ return -ENOENT;
+ }
+ mode = obj_to_mode(obj);
+ }
+
+ /* FIXME should check just the user timings? */
+ if (crtc->enabled && mode && !memcmp(&crtc->mode, mode, sizeof *mode))
+ return 0;
+
+ /* turn on/off or active area changed? */
+ if (!crtc->enabled || !mode ||
+ crtc->mode.hdisplay != mode->hdisplay ||
+ crtc->mode.vdisplay != mode->vdisplay)
+ state->active_dirty = true;
+
+ if (mode) {
+ crtc->mode = *mode;
+ crtc->enabled = true;
+ } else
+ crtc->enabled = false;
+ state->mode_dirty = true;
+ } else if (prop == prop_fb_id) {
+ struct drm_framebuffer *fb = NULL;
+
+ if (crtc->fb) {
+ if (value == crtc->fb->base.id)
+ return 0;
+ } else {
+ if (value == 0)
+ return 0;
+ }
+
+ if (value) {
+ obj = drm_mode_object_find(crtc->dev, value, DRM_MODE_OBJECT_FB);
+ if (!obj) {
+ printk("Unknown framebuffer ID %llu\n", value);
+ return -ENOENT;
+ }
+ fb = obj_to_fb(obj);
+ }
+
+ crtc->fb = fb;
+ if (crtc_funcs->mode_set_base)
+ state->fb_dirty = true;
+ else
+ state->mode_dirty = true;
+ } else if (prop == prop_connector_ids) {
+ const uint32_t *ids = blob_data;
+ uint32_t count_ids = value / sizeof(uint32_t);
+ int ret;
+
+ if (value & 3)
+ return -EINVAL;
+
+ if (count_ids > crtc->dev->mode_config.num_connector)
+ return -ERANGE;
+
+ ret = process_connectors(state, ids, count_ids);
+ if (ret)
+ return ret;
+ } else
+ return -ENOENT;
+
+ s->dirty = true;
+
+ return 0;
+}
+
+static struct intel_plane_state *get_plane_state(const struct drm_device *dev,
+ struct intel_atomic_state *state,
+ const struct drm_plane *plane)
+{
+ int i;
+
+ for (i = 0; i < dev->mode_config.num_plane; i++)
+ if (plane == state->plane[i].plane)
+ return &state->plane[i];
+
+ return NULL;
+}
+
+static struct intel_crtc_state *get_crtc_state(const struct drm_device *dev,
+ struct intel_atomic_state *state,
+ const struct drm_crtc *crtc)
+{
+ int i;
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++)
+ if (crtc == state->crtc[i].crtc)
+ return &state->crtc[i];
+
+ return NULL;
+}
+
+static void crtc_prepare(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder *encoder;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs->prepare(encoder);
+ }
+
+ drm_crtc_prepare_encoders(dev);
+
+ crtc_funcs->prepare(crtc);
+}
+
+static int crtc_set_base(struct drm_crtc *crtc,
+ struct drm_framebuffer *old_fb)
+{
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ return crtc_funcs->mode_set_base(crtc, crtc->x, crtc->y, old_fb);
+}
+
+static int crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_framebuffer *old_fb)
+{
+ struct drm_device *dev = crtc->dev;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder *encoder;
+ int ret;
+
+ ret = crtc_funcs->mode_set(crtc, &crtc->mode, &crtc->hwmode,
+ crtc->x, crtc->y, old_fb);
+ if (ret)
+ return ret;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs->mode_set(encoder, &crtc->mode, &crtc->hwmode);
+ }
+
+ return 0;
+}
+
+static void crtc_commit(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+ struct drm_encoder *encoder;
+
+ crtc_funcs->commit(crtc);
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+ if (encoder->crtc != crtc)
+ continue;
+
+ encoder_funcs->commit(encoder);
+ }
+}
+
+int intel_commit_plane(struct drm_plane *plane,
+ struct drm_crtc *crtc,
+ struct drm_framebuffer *fb,
+ const struct intel_plane_coords *st);
+
+static int apply_config(struct drm_device *dev,
+ struct intel_atomic_state *s)
+{
+ int i, ret;
+ struct drm_plane *plane;
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+
+ if (!st->mode_dirty)
+ continue;
+
+ crtc_prepare(st->crtc);
+ }
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+
+ if (st->mode_dirty) {
+ ret = crtc_mode_set(st->crtc, st->old_fb);
+ if (ret)
+ return ret;
+ } else if (st->fb_dirty) {
+ ret = crtc_set_base(st->crtc, st->old_fb);
+ if (ret)
+ return ret;
+ } else
+ continue;
+
+ /*
+ * Old fb was unpinned and new fb pinned.
+ * Update the old_fb pointer accordingingly
+ * in case we need to roll back later.
+ */
+ st->old_fb = st->crtc->fb;
+ }
+
+ for (i = 0; i < dev->mode_config.num_plane; i++) {
+ struct intel_plane_state *st = &s->plane[i];
+ struct intel_crtc_state *cst;
+ struct drm_plane *plane = st->plane;
+
+ if (!s->plane[i].dirty)
+ continue;
+
+ if (!plane->crtc)
+ continue;
+
+ cst = get_crtc_state(dev, s, plane->crtc);
+ /* already handled by the CRTC mode set? */
+ if (cst->mode_dirty)
+ continue;
+
+ ret = intel_commit_plane(plane, plane->crtc, plane->fb, &st->coords);
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+
+ if (!st->mode_dirty)
+ continue;
+
+ crtc_commit(st->crtc);
+ }
+
+ /*
+ * FIXME perhaps better order would be
+ * 1. prepare all current objects
+ * 2. disable unused objects
+ * 3. set mode for current objects
+ * 4. commit current objects
+ */
+ drm_helper_disable_unused_functions(dev);
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ /* planes attached to crtcs already handled in mode_set() */
+ if (!plane->crtc || !plane->fb)
+ plane->funcs->disable_plane(plane);
+ }
+
+ /* don't restore the old state in end() */
+ s->dirty = false;
+
+ return 0;
+}
+
+static void restore_state(struct drm_device *dev,
+ struct intel_atomic_state *s)
+{
+ int i;
+ struct drm_connector *connector;
+ struct drm_encoder *encoder;
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+
+ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head)
+ *connector = s->saved_connectors[i++];
+ i = 0;
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head)
+ *encoder = s->saved_encoders[i++];
+ i = 0;
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ *crtc = s->saved_crtcs[i++];
+ i = 0;
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head)
+ *plane = s->saved_planes[i++];
+
+ /* FIXME props etc. */
+
+ /* was the hardware state clobbered? */
+ if (s->restore_hw)
+ apply_config(dev, s);
+}
+
+static int intel_atomic_set(struct drm_device *dev, void *state,
+ struct drm_mode_object *obj,
+ struct drm_property *prop,
+ uint64_t value, void *blob_data)
+{
+ struct intel_atomic_state *s = state;
+ int ret = -EINVAL;
+
+ switch (obj->type) {
+ case DRM_MODE_OBJECT_PLANE:
+ ret = plane_set(s, get_plane_state(dev, s, obj_to_plane(obj)), prop, value);
+ break;
+ case DRM_MODE_OBJECT_CRTC:
+ ret = crtc_set(s, get_crtc_state(dev, s, obj_to_crtc(obj)), prop, value, blob_data);
+ break;
+ default:
+ break;
+ }
+
+ kfree(blob_data);
+
+ return ret;
+}
+
+int intel_check_plane(const struct drm_plane *plane,
+ const struct drm_crtc *crtc,
+ const struct drm_framebuffer *fb,
+ struct intel_plane_coords *st);
+
+static void dirty_planes(const struct drm_device *dev,
+ struct intel_atomic_state *state,
+ const struct drm_crtc *crtc)
+{
+ int i;
+
+ for (i = 0; i < dev->mode_config.num_plane; i++) {
+ struct intel_plane_state *s = &state->plane[i];
+
+ if (s->plane->crtc == crtc)
+ s->dirty = true;
+ }
+}
+
+static int check_crtc(struct intel_crtc_state *s, bool mode_dirty)
+{
+ struct drm_crtc *crtc = s->crtc;
+ struct drm_device *dev = crtc->dev;
+ struct drm_encoder *encoder;
+ struct drm_framebuffer *fb = crtc->fb;
+ struct drm_display_mode mode, adjusted_mode;
+ const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+
+ /* must have a fb and connectors if we have a mode, and vice versa */
+ if (crtc->enabled) {
+ if (!fb)
+ return -EINVAL;
+ if (!drm_helper_crtc_in_use(crtc))
+ return -EINVAL;
+ } else {
+ if (fb)
+ return -EINVAL;
+ if (drm_helper_crtc_in_use(crtc))
+ return -EINVAL;
+ }
+
+ if (crtc->enabled) {
+ if (crtc->mode.hdisplay > fb->width ||
+ crtc->mode.vdisplay > fb->height ||
+ crtc->x > fb->width - crtc->mode.hdisplay ||
+ crtc->y > fb->height - crtc->mode.vdisplay)
+ return -ENOSPC;
+ }
+
+ if (!crtc->enabled || !mode_dirty)
+ return 0;
+
+ mode = adjusted_mode = crtc->mode;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ const struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+
+ if (!encoder_funcs->mode_fixup(encoder, &mode, &adjusted_mode))
+ return -EINVAL;
+ }
+
+ if (!crtc_funcs->mode_fixup(crtc, &mode, &adjusted_mode))
+ return -EINVAL;
+
+ adjusted_mode.clock = mode.clock;
+
+ crtc->hwmode = adjusted_mode;
+
+ return 0;
+}
+
+static int intel_atomic_check(struct drm_device *dev, void *state)
+{
+ struct intel_atomic_state *s = state;
+ int ret;
+ int i;
+
+ if (!s->dirty)
+ return 0;
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+
+ if (!st->fb_dirty && !st->mode_dirty)
+ continue;
+
+ ret = check_crtc(st, st->mode_dirty);
+ if (ret)
+ return ret;
+
+ /*
+ * Mark all planes on this CRTC as dirty if the active video
+ * area changed so that the planes will get reclipped correctly.
+ */
+ if (st->active_dirty)
+ dirty_planes(dev, s, st->crtc);
+ }
+
+ /* check for conflicts in encoder/connector assignment */
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+ int j;
+
+ for (j = i + 1; j < dev->mode_config.num_crtc; j++) {
+ struct intel_crtc_state *st2 = &s->crtc[j];
+
+ if (st->connectors_bitmask & st2->connectors_bitmask)
+ return -EINVAL;
+
+ if (st->encoders_bitmask & st2->encoders_bitmask)
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < dev->mode_config.num_plane; i++) {
+ struct intel_plane_state *st = &s->plane[i];
+ const struct drm_plane *plane = st->plane;
+
+ if (!st->dirty)
+ continue;
+
+ st->coords.crtc_x = plane->crtc_x;
+ st->coords.crtc_y = plane->crtc_y;
+ st->coords.crtc_w = plane->crtc_w;
+ st->coords.crtc_h = plane->crtc_h;
+
+ st->coords.src_x = plane->src_x;
+ st->coords.src_y = plane->src_y;
+ st->coords.src_w = plane->src_w;
+ st->coords.src_h = plane->src_h;
+
+ ret = intel_check_plane(plane, plane->crtc, plane->fb, &st->coords);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void update_plane_props(struct drm_plane *plane)
+{
+ struct drm_mode_object *obj = &plane->base;
+
+ drm_object_property_set_value(obj, prop_src_x, plane->src_x);
+ drm_object_property_set_value(obj, prop_src_y, plane->src_y);
+ drm_object_property_set_value(obj, prop_src_w, plane->src_w);
+ drm_object_property_set_value(obj, prop_src_h, plane->src_h);
+
+ drm_object_property_set_value(obj, prop_crtc_x, plane->crtc_x);
+ drm_object_property_set_value(obj, prop_crtc_y, plane->crtc_y);
+ drm_object_property_set_value(obj, prop_crtc_w, plane->crtc_w);
+ drm_object_property_set_value(obj, prop_crtc_h, plane->crtc_h);
+
+ drm_object_property_set_value(obj, prop_fb_id, plane->fb ? plane->fb->base.id : 0);
+ drm_object_property_set_value(obj, prop_crtc_id, plane->crtc ? plane->crtc->base.id : 0);
+}
+
+static int free_connector_ids(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_mode_object *obj;
+ uint64_t value;
+ int ret;
+
+ ret = drm_object_property_get_value(&crtc->base, prop_connector_ids, &value);
+ if (ret)
+ return ret;
+
+ if (!value)
+ return 0;
+
+ obj = drm_mode_object_find(dev, value, DRM_MODE_PROP_BLOB);
+ if (!obj)
+ return -ENOENT;
+
+ drm_property_destroy_blob(dev, obj_to_blob(obj));
+
+ return 0;
+}
+
+static int update_connector_ids(struct drm_crtc *crtc)
+{
+ struct drm_device *dev = crtc->dev;
+ struct drm_connector *connector;
+ struct drm_property_blob *blob;
+ uint64_t value = 0;
+ int i = 0;
+ uint32_t connector_ids[dev->mode_config.num_connector];
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ if (connector->encoder && connector->encoder->crtc == crtc)
+ connector_ids[i++] = connector->base.id;
+ }
+
+ if (i) {
+ /*
+ * FIXME make blobs into vectors, and pre-allocate using worst case estimate.
+ * That way this function could never fail.
+ */
+ blob = drm_property_create_blob(dev, i * sizeof connector_ids[0], connector_ids);
+ if (!blob)
+ return -ENOMEM;
+
+ value = blob->base.id;
+ }
+
+ free_connector_ids(crtc);
+
+ drm_object_property_set_value(&crtc->base, prop_connector_ids, value);
+
+ return 0;
+}
+
+static void update_crtc_props(struct drm_crtc *crtc)
+{
+ struct drm_mode_object *obj = &crtc->base;
+
+ drm_object_property_set_value(obj, prop_src_x, crtc->x);
+ drm_object_property_set_value(obj, prop_src_y, crtc->y);
+
+ drm_object_property_set_value(obj, prop_fb_id, crtc->fb ? crtc->fb->base.id : 0);
+
+ /*
+ * FIXME if mode is user specifiec via old setcrtc ioctl prop value should be -1
+ * or perhaps setcrtc should insert an imlicitly created mode into some list...
+ */
+ drm_object_property_set_value(obj, prop_mode_id,
+ crtc->enabled ? crtc->mode.base.id : 0);
+
+ update_connector_ids(crtc);
+}
+
+static void update_props(const struct drm_device *dev,
+ struct intel_atomic_state *s)
+{
+ int i;
+
+ for (i = 0; i < dev->mode_config.num_crtc; i++) {
+ struct intel_crtc_state *st = &s->crtc[i];
+
+ if (!st->fb_dirty && !st->mode_dirty)
+ continue;
+
+ update_crtc_props(st->crtc);
+ }
+
+ for (i = 0; i < dev->mode_config.num_plane; i++) {
+ struct intel_plane_state *st = &s->plane[i];
+
+ if (!st->dirty)
+ continue;
+
+ update_plane_props(st->plane);
+ }
+}
+
+static int intel_atomic_commit(struct drm_device *dev, void *state)
+{
+ struct intel_atomic_state *s = state;
+ int ret;
+
+ if (!s->dirty)
+ return 0;
+
+ ret = apply_config(dev, s);
+ if (ret) {
+ s->restore_hw = true;
+ return ret;
+ }
+
+ update_props(dev, s);
+
+ return 0;
+}
+
+static void intel_atomic_end(struct drm_device *dev, void *state)
+{
+ struct intel_atomic_state *s = state;
+
+ /* restore the state of all objects */
+ if (s->dirty)
+ restore_state(dev, state);
+
+ kfree(state);
+}
+
+static const struct drm_atomic_funcs intel_atomic_funcs = {
+ .begin = intel_atomic_begin,
+ .set = intel_atomic_set,
+ .check = intel_atomic_check,
+ .commit = intel_atomic_commit,
+ .end = intel_atomic_end,
+};
+
+int intel_atomic_init(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+ struct drm_plane *plane;
+ int ret = -ENOMEM;
+
+ prop_src_x = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX);
+ if (!prop_src_x)
+ goto out;
+ prop_src_y = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX);
+ if (!prop_src_y)
+ goto destroy_src_x;
+ prop_src_w = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX);
+ if (!prop_src_w)
+ goto destroy_src_y;
+ prop_src_h = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX);
+ if (!prop_src_h)
+ goto destroy_src_w;
+
+ prop_crtc_x = drm_property_create_range(dev, 0, "CRTC_X", INT_MIN, INT_MAX);
+ if (!prop_crtc_x)
+ goto destroy_src_h;
+ prop_crtc_y = drm_property_create_range(dev, 0, "CRTC_Y", INT_MIN, INT_MAX);
+ if (!prop_crtc_y)
+ goto destroy_crtc_x;
+ prop_crtc_w = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX);
+ if (!prop_crtc_w)
+ goto destroy_crtc_y;
+ prop_crtc_h = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX);
+ if (!prop_crtc_h)
+ goto destroy_crtc_w;
+
+ /* FIXME create special object ID property type? */
+ prop_fb_id = drm_property_create_range(dev, 0, "FB_ID", 0, UINT_MAX);
+ if (!prop_fb_id)
+ goto destroy_crtc_h;
+ prop_crtc_id = drm_property_create_range(dev, 0, "CRTC_ID", 0, UINT_MAX);
+ if (!prop_crtc_id)
+ goto destroy_fb_id;
+ prop_mode_id = drm_property_create_range(dev, 0, "MODE_ID", 0, UINT_MAX);
+ if (!prop_mode_id)
+ goto destroy_crtc_id;
+
+ /* FIXME create special object ID list property type? */
+ prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0);
+ if (!prop_connector_ids)
+ goto destroy_mode_id;
+
+ list_for_each_entry(plane, &dev->mode_config.plane_list, head) {
+ struct drm_mode_object *obj = &plane->base;
+
+ drm_object_attach_property(obj, prop_src_x, 0);
+ drm_object_attach_property(obj, prop_src_y, 0);
+ drm_object_attach_property(obj, prop_src_w, 0);
+ drm_object_attach_property(obj, prop_src_h, 0);
+
+ drm_object_attach_property(obj, prop_crtc_x, 0);
+ drm_object_attach_property(obj, prop_crtc_y, 0);
+ drm_object_attach_property(obj, prop_crtc_w, 0);
+ drm_object_attach_property(obj, prop_crtc_h, 0);
+
+ drm_object_attach_property(obj, prop_fb_id, 0);
+ drm_object_attach_property(obj, prop_crtc_id, 0);
+
+ update_plane_props(plane);
+ }
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct drm_mode_object *obj = &crtc->base;
+
+ drm_object_attach_property(obj, prop_src_x, 0);
+ drm_object_attach_property(obj, prop_src_y, 0);
+
+ drm_object_attach_property(obj, prop_fb_id, 0);
+ drm_object_attach_property(obj, prop_mode_id, 0);
+ drm_object_attach_property(obj, prop_connector_ids, 0);
+
+ update_crtc_props(crtc);
+ }
+
+ dev->driver->atomic_funcs = &intel_atomic_funcs;
+
+ return 0;
+
+ destroy_mode_id:
+ drm_property_destroy(dev, prop_mode_id);
+ destroy_crtc_id:
+ drm_property_destroy(dev, prop_crtc_id);
+ destroy_fb_id:
+ drm_property_destroy(dev, prop_fb_id);
+ destroy_crtc_h:
+ drm_property_destroy(dev, prop_crtc_h);
+ destroy_crtc_w:
+ drm_property_destroy(dev, prop_crtc_w);
+ destroy_crtc_y:
+ drm_property_destroy(dev, prop_crtc_y);
+ destroy_crtc_x:
+ drm_property_destroy(dev, prop_crtc_x);
+ destroy_src_h:
+ drm_property_destroy(dev, prop_src_h);
+ destroy_src_w:
+ drm_property_destroy(dev, prop_src_w);
+ destroy_src_y:
+ drm_property_destroy(dev, prop_src_y);
+ destroy_src_x:
+ drm_property_destroy(dev, prop_src_x);
+ out:
+ return ret;
+}
+
+void intel_atomic_fini(struct drm_device *dev)
+{
+ struct drm_crtc *crtc;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
+ free_connector_ids(crtc);
+
+ drm_property_destroy(dev, prop_connector_ids);
+ drm_property_destroy(dev, prop_mode_id);
+ drm_property_destroy(dev, prop_crtc_id);
+ drm_property_destroy(dev, prop_fb_id);
+
+ drm_property_destroy(dev, prop_crtc_h);
+ drm_property_destroy(dev, prop_crtc_w);
+ drm_property_destroy(dev, prop_crtc_y);
+ drm_property_destroy(dev, prop_crtc_x);
+
+ drm_property_destroy(dev, prop_src_h);
+ drm_property_destroy(dev, prop_src_w);
+ drm_property_destroy(dev, prop_src_y);
+ drm_property_destroy(dev, prop_src_x);
+}
diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
index 83437ee..4b186d6 100644
--- a/drivers/gpu/drm/i915/intel_display.c
+++ b/drivers/gpu/drm/i915/intel_display.c
@@ -7053,6 +7053,9 @@ void intel_modeset_init_hw(struct drm_device *dev)
ivb_pch_pwm_override(dev);
}
+void intel_atomic_init(struct drm_device *dev);
+void intel_atomic_fini(struct drm_device *dev);
+
void intel_modeset_init(struct drm_device *dev)
{
struct drm_i915_private *dev_priv = dev->dev_private;
@@ -7107,6 +7110,8 @@ void intel_modeset_init(struct drm_device *dev)
INIT_WORK(&dev_priv->idle_work, intel_idle_update);
setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
(unsigned long)dev);
+
+ intel_atomic_init(dev);
}
void intel_modeset_gem_init(struct drm_device *dev)
@@ -7122,6 +7127,8 @@ void intel_modeset_cleanup(struct drm_device *dev)
struct drm_crtc *crtc;
struct intel_crtc *intel_crtc;
+ intel_atomic_fini(dev);
+
drm_kms_helper_poll_fini(dev);
mutex_lock(&dev->struct_mutex);
--
1.7.3.4
More information about the dri-devel
mailing list