From: Ville Syrjälä <ville.syrjala@xxxxxxxxxxxxxxx> Just a quick prototype to test the atomic modeset API. Implemented via already existing non-atomic mechanisms internally. --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/intel_atomic.c | 788 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 7 + 3 files changed, 796 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..a6643fd --- /dev/null +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -0,0 +1,788 @@ +/* + */ + +#include <drm/drmP.h> +#include <drm/drm_crtc.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; +/* FIXME must be per-CRTC */ +static struct drm_property_blob *blob_connector_ids; + +static uint32_t *connector_ids; + +struct intel_plane_state { + struct drm_plane *plane; + int32_t crtc_x, crtc_y; + uint32_t crtc_w, crtc_h; + uint32_t src_x, src_y, src_w, src_h; + struct drm_framebuffer *fb; + struct drm_crtc *crtc; + uint32_t fb_id; + uint32_t crtc_id; + bool dirty; +}; + +struct intel_crtc_state { + struct drm_crtc *crtc; + struct drm_display_mode *mode; + int x, y; + struct drm_framebuffer *fb; + uint32_t fb_id; + bool mode_dirty; + bool fb_dirty; + bool active_dirty; + unsigned int count_connectors; + struct drm_connector **connectors; +}; + +struct intel_atomic_state { + struct intel_plane_state plane[3]; + struct intel_crtc_state crtc[3]; + bool fbs_pinned; + bool dirty; +}; + +static int populate_connectors(const struct drm_crtc *crtc, + struct drm_connector **connector_list) +{ + struct drm_connector *connector; + + int i = 0; + + list_for_each_entry(connector, &crtc->dev->mode_config.connector_list, head) { + if (!connector->encoder || connector->encoder->crtc != crtc) + continue; + + connector_list[i++] = connector; + } + + return i; +} + +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_ptr; + int i; + + state = kzalloc(sizeof *state + dev->mode_config.num_connector * + sizeof(struct drm_connector *) * ARRAY_SIZE(state->crtc), GFP_KERNEL); + if (!state) + return ERR_PTR(-ENOMEM); + + connector_ptr = (struct drm_connector **)(state + 1); + + i = 0; + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + if (i >= ARRAY_SIZE(state->crtc)) + break; + + state->crtc[i].crtc = crtc; + state->crtc[i].mode = &crtc->mode; + state->crtc[i].x = crtc->x; + state->crtc[i].y = crtc->y; + state->crtc[i].fb = crtc->fb; + state->crtc[i].fb_id = crtc->fb ? crtc->fb->base.id : 0; + + state->crtc[i].count_connectors = populate_connectors(crtc, connector_ptr); + state->crtc[i].connectors = connector_ptr; + connector_ptr += dev->mode_config.num_connector; + + i++; + } + + i = 0; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + if (i >= ARRAY_SIZE(state->plane)) + break; + + state->plane[i].plane = plane; + state->plane[i].crtc_x = plane->crtc_x; + state->plane[i].crtc_y = plane->crtc_y; + state->plane[i].crtc_w = plane->crtc_w; + state->plane[i].crtc_h = plane->crtc_h; + state->plane[i].src_x = plane->src_x; + state->plane[i].src_y = plane->src_y; + state->plane[i].src_w = plane->src_w; + state->plane[i].src_h = plane->src_h; + state->plane[i].fb = plane->fb; + state->plane[i].fb_id = plane->fb ? plane->fb->base.id : 0; + state->plane[i].crtc = plane->crtc; + state->plane[i].crtc_id = plane->crtc ? plane->crtc->base.id : 0; + + i++; + } + + return state; +} + +static int plane_set(struct intel_plane_state *state, + struct drm_property *prop, + uint64_t value) +{ + struct drm_plane *plane = state->plane; + struct drm_mode_object *obj; + + if (!state) + return -ENOENT; + + if (prop == prop_src_x) { + if (state->src_x == value) + return 0; + state->src_x = value; + } else if (prop == prop_src_y) { + if (state->src_y == value) + return 0; + state->src_y = value; + } else if (prop == prop_src_w) { + if (state->src_w == value) + return 0; + state->src_w = value; + } else if (prop == prop_src_h) { + if (state->src_h == value) + return 0; + state->src_h = value; + } else if (prop == prop_crtc_x) { + if (state->crtc_x == value) + return 0; + state->crtc_x = value; + } else if (prop == prop_crtc_y) { + if (state->crtc_y == value) + return 0; + state->crtc_y = value; + } else if (prop == prop_crtc_w) { + if (state->crtc_w == value) + return 0; + state->crtc_w = value; + } else if (prop == prop_crtc_h) { + if (state->crtc_h == value) + return 0; + state->crtc_h = value; + } else if (prop == prop_crtc_id) { + struct drm_crtc *crtc = NULL; + + if (state->crtc_id == value) + 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); + } + + state->crtc = crtc; + state->crtc_id = value; + } else if (prop == prop_fb_id) { + struct drm_framebuffer *fb = NULL; + + if (state->fb_id == value) + 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); + } + + state->fb = fb; + state->fb_id = value; + } else + return -ENOENT; + + state->dirty = true; + + return 0; +} + +static int crtc_set(struct intel_crtc_state *state, + struct drm_property *prop, + uint64_t value, void *blob_data) +{ + struct drm_crtc *crtc = state->crtc; + struct drm_mode_object *obj; + + if (!state) + return -ENOENT; + + if (prop == prop_src_x) { + if (state->x == value) + return 0; + state->x = value; + state->mode_dirty = true; + } else if (prop == prop_src_y) { + if (state->y == value) + return 0; + state->y = value; + state->mode_dirty = true; + } else if (prop == prop_mode_id) { + struct drm_display_mode *mode = NULL; + + 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); + } + + if (!state->mode && !mode) + return 0; + + if (state->mode && mode && !memcmp(state->mode, mode, sizeof *mode)) + return 0; + + if (!state->mode || !mode || + state->mode->hdisplay != mode->hdisplay || + state->mode->vdisplay != mode->vdisplay) + state->active_dirty = true; + + state->mode = mode; + state->mode_dirty = true; + } else if (prop == prop_fb_id) { + struct drm_framebuffer *fb = NULL; + + if (state->fb_id == value) + 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); + } + + if (state->fb && fb) { + /* can we page flip? */ + if (state->fb->pixel_format == fb->pixel_format && + state->fb->offsets[0] == fb->offsets[0] && + state->fb->pitches[0] == fb->pitches[0]) { + state->fb_id = value; + state->fb = obj_to_fb(obj); + state->fb_dirty = true; + return 0; + } + } + + state->fb_id = value; + state->fb = fb; + state->mode_dirty = true; + } else if (prop == prop_connector_ids) { + const uint32_t *new_conn = blob_data; + uint32_t count_new_conn = value / sizeof(uint32_t); + unsigned int i; + + if (value & 3) + return -EINVAL; + + if (count_new_conn > crtc->dev->mode_config.num_connector) + return -ERANGE; + + if (count_new_conn == state->count_connectors) { + for (i = 0; i < count_new_conn; i++) { + if (state->connectors[i]->base.id != new_conn[i]) + break; + } + /* no change */ + if (i == count_new_conn) + return 0; + } + + if (!count_new_conn) { + state->count_connectors = 0; + state->mode_dirty = true; + return 0; + } + + for (i = 0; i < count_new_conn; i++) { + obj = drm_mode_object_find(crtc->dev, new_conn[i], DRM_MODE_OBJECT_CONNECTOR); + if (!obj) { + printk("Unknown connector ID %u\n", new_conn[i]); + return -ENOENT; + } + + state->connectors[i] = obj_to_connector(obj); + } + state->count_connectors = count_new_conn; + state->mode_dirty = true; + } else + return -ENOENT; + + return 0; +} + +static struct intel_plane_state *get_plane_state(struct intel_atomic_state *state, + struct drm_plane *plane) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(state->plane); i++) + if (plane == state->plane[i].plane) + return &state->plane[i]; + + return NULL; +} + +static struct intel_crtc_state *get_crtc_state(struct intel_atomic_state *state, + struct drm_crtc *crtc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(state->crtc); i++) + if (crtc == state->crtc[i].crtc) + return &state->crtc[i]; + + return NULL; +} + +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(get_plane_state(s, obj_to_plane(obj)), prop, value); + break; + case DRM_MODE_OBJECT_CRTC: + ret = crtc_set(get_crtc_state(s, obj_to_crtc(obj)), prop, value, blob_data); + break; + default: + break; + } + + kfree(blob_data); + + if (ret) + return ret; + + s->dirty = true; + + return 0; +} + +static int check_plane(struct intel_plane_state *state) +{ + /* FIXME check everything */ + + return 0; +} + +static void dirty_planes(struct intel_atomic_state *state, + struct drm_crtc *crtc) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(state->plane); i++) { + if (state->plane[i].crtc == crtc) + state->plane[i].dirty = true; + } +} + +static int check_crtc(struct intel_atomic_state *state, + struct intel_crtc_state *s) +{ + /* + * Mark all planes on this CRTC as dirty if the active video + * area changed so that the planes will get reclipped correctly. + */ + if (s->active_dirty) + dirty_planes(state, s->crtc); + + /* FIXME check mode and fb if dirty */ + + return 0; +} + +static int intel_atomic_check(struct drm_device *dev, void *state) +{ + struct intel_atomic_state *s = state; + int i; + + if (!s->dirty) + return 0; + + for (i = 0; i < ARRAY_SIZE(s->crtc); i++) { + if (!s->crtc[i].fb_dirty && !s->crtc[i].mode_dirty) + continue; + + check_crtc(s, &s->crtc[i]); + } + + for (i = 0; i < ARRAY_SIZE(s->plane); i++) { + if (!s->plane[i].dirty) + continue; + + check_plane(&s->plane[i]); + } + + return 0; +} + +static void update_plane_props(struct intel_plane_state *state) +{ + struct drm_mode_object *obj = &state->plane->base; + + if (prop_src_x) + drm_object_property_set_value(obj, prop_src_x, state->src_x); + if (prop_src_y) + drm_object_property_set_value(obj, prop_src_y, state->src_y); + if (prop_src_w) + drm_object_property_set_value(obj, prop_src_w, state->src_w); + if (prop_src_h) + drm_object_property_set_value(obj, prop_src_h, state->src_h); + + if (prop_crtc_x) + drm_object_property_set_value(obj, prop_crtc_x, state->crtc_x); + if (prop_crtc_y) + drm_object_property_set_value(obj, prop_crtc_y, state->crtc_y); + if (prop_crtc_w) + drm_object_property_set_value(obj, prop_crtc_w, state->crtc_w); + if (prop_crtc_h) + drm_object_property_set_value(obj, prop_crtc_h, state->crtc_h); + + if (prop_fb_id) + drm_object_property_set_value(obj, prop_fb_id, state->fb_id); + + if (prop_crtc_id) + drm_object_property_set_value(obj, prop_crtc_id, state->crtc_id); +} + +static void update_crtc_props(struct intel_crtc_state *state) +{ + struct drm_mode_object *obj = &state->crtc->base; + struct drm_device *dev = state->crtc->dev; + unsigned int i; + + if (prop_src_x) + drm_object_property_set_value(obj, prop_src_x, state->x); + if (prop_src_y) + drm_object_property_set_value(obj, prop_src_y, state->y); + + if (prop_fb_id) + drm_object_property_set_value(obj, prop_fb_id, state->fb_id); + + /* FIXME if mode is user specifiec via old setcrtc ioctl prop value should be -1 */ + if (prop_mode_id) + drm_object_property_set_value(obj, prop_mode_id, + state->mode ? state->mode->base.id : 0); + + if (!prop_connector_ids || !connector_ids) + return; + + if (blob_connector_ids) + drm_property_destroy_blob(dev, blob_connector_ids); + + for (i = 0; i < state->count_connectors; i++) + connector_ids[i] = state->connectors[i]->base.id; + + blob_connector_ids = drm_property_create_blob(dev, + state->count_connectors * sizeof connector_ids[0], + connector_ids); + if (!blob_connector_ids) + drm_object_property_set_value(obj, prop_connector_ids, + blob_connector_ids->base.id); + else + drm_object_property_set_value(obj, prop_connector_ids, 0); +} + +static void update_props(struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(s->crtc); i++) { + if (!s->crtc[i].fb_dirty && !s->crtc[i].mode_dirty) + continue; + + update_crtc_props(&s->crtc[i]); + } + + for (i = 0; i < ARRAY_SIZE(s->plane); i++) { + if (!s->plane[i].dirty) + continue; + + update_plane_props(&s->plane[i]); + } +} + +static int update_crtc(struct intel_crtc_state *state) +{ + struct drm_crtc *crtc = state->crtc; + struct drm_mode_set set; + int i; + + if (!state->mode_dirty) + return crtc->funcs->page_flip(crtc, state->fb, NULL); + + set.crtc = state->crtc; + set.x = state->x; + set.y = state->y; + set.mode = state->mode; + set.connectors = state->connectors; + set.num_connectors = state->count_connectors; + set.fb = state->fb; + +#if 0 + printk("CRTC %u\n" + " x = %d\n" + " y = %d\n", + crtc->base.id, + set.x, + set.y); + + if (set.mode) { + printk(" mode %u\n" + " hdisplay = %d\n" + " hsync_start = %d\n" + " hsync_end = %d\n" + " htotal = %d\n" + " hdisplay = %d\n" + " hsync_start = %d\n" + " hsync_end = %d\n" + " htotal = %d\n" + " clock = %d\n" + " vrefresh = %d\n", + set.mode->base.id, + set.mode->hdisplay, + set.mode->hsync_start, + set.mode->hsync_end, + set.mode->htotal, + set.mode->vdisplay, + set.mode->vsync_start, + set.mode->vsync_end, + set.mode->vtotal, + set.mode->clock, + set.mode->vrefresh); + } else + printk(" no mode\n"); + + if (set.num_connectors) { + printk(" connectors:"); + for (i = 0; i < set.num_connectors; i++) + printk(" %u", set.connectors[i]->base.id); + printk("\n"); + } else + printk(" no connectors\n"); +#endif + + return crtc->funcs->set_config(&set); +} + +static int update_plane(struct intel_plane_state *state) +{ + struct drm_plane *plane = state->plane; + int ret; + + if (state->fb && state->crtc) + ret = plane->funcs->update_plane(plane, + state->crtc, + state->fb, + state->crtc_x, + state->crtc_y, + state->crtc_w, + state->crtc_h, + state->src_x, + state->src_y, + state->src_w, + state->src_h); + else + ret = plane->funcs->disable_plane(plane); + + if (ret) + return ret; + + plane->fb = state->fb; + plane->crtc = state->crtc; + plane->crtc_x = state->crtc_x; + plane->crtc_y = state->crtc_y; + plane->crtc_w = state->crtc_w; + plane->crtc_h = state->crtc_h; + plane->src_x = state->src_x; + plane->src_y = state->src_y; + plane->src_w = state->src_w; + plane->src_h = state->src_h; + + return 0; +} + +static int intel_atomic_commit(struct drm_device *dev, void *state) +{ + struct intel_atomic_state *s = state; + int i; + int ret = 0; + + if (!s->dirty) + return 0; + + for (i = 0; i < ARRAY_SIZE(s->crtc); i++) { + if (!s->crtc[i].mode_dirty) + continue; + + //intel_disable_crtc(); + } + + for (i = 0; i < ARRAY_SIZE(s->plane); i++) { + if (!s->plane[i].dirty) + continue; + + ret = update_plane(&s->plane[i]); + if (ret) + return ret; + } + + for (i = 0; i < ARRAY_SIZE(s->crtc); i++) { + if (!s->crtc[i].mode_dirty && !s->crtc[i].fb_dirty) + continue; + + ret = update_crtc(&s->crtc[i]); + if (ret) + return ret; + } + + update_props(s); + + return ret; +} + +static void intel_atomic_end(struct drm_device *dev, void *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; + void *state; + + connector_ids = kcalloc(dev->mode_config.num_connector, sizeof connector_ids[0], GFP_KERNEL); + if (!connector_ids) + return -ENOMEM; + + prop_src_x = drm_property_create_range(dev, 0, "SRC_X", 0, UINT_MAX); + prop_src_y = drm_property_create_range(dev, 0, "SRC_Y", 0, UINT_MAX); + prop_src_w = drm_property_create_range(dev, 0, "SRC_W", 0, UINT_MAX); + prop_src_h = drm_property_create_range(dev, 0, "SRC_H", 0, UINT_MAX); + + // fixme signed values needed + prop_crtc_x = drm_property_create_range(dev, 0, "CRTC_X", INT_MIN, INT_MAX); + prop_crtc_y = drm_property_create_range(dev, 0, "CRTC_Y", INT_MIN, INT_MAX); + prop_crtc_w = drm_property_create_range(dev, 0, "CRTC_W", 0, INT_MAX); + prop_crtc_h = drm_property_create_range(dev, 0, "CRTC_H", 0, INT_MAX); + + prop_fb_id = drm_property_create_range(dev, 0, "FB_ID", 0, UINT_MAX); + prop_crtc_id = drm_property_create_range(dev, 0, "CRTC_ID", 0, UINT_MAX); + prop_mode_id = drm_property_create_range(dev, 0, "MODE_ID", 0, UINT_MAX); + + prop_connector_ids = drm_property_create(dev, DRM_MODE_PROP_BLOB, "CONNECTOR_IDS", 0); + + list_for_each_entry(plane, &dev->mode_config.plane_list, head) { + struct drm_mode_object *obj = &plane->base; + + if (prop_src_x) + drm_object_attach_property(obj, prop_src_x, 0); + if (prop_src_y) + drm_object_attach_property(obj, prop_src_y, 0); + if (prop_src_w) + drm_object_attach_property(obj, prop_src_w, 0); + if (prop_src_h) + drm_object_attach_property(obj, prop_src_h, 0); + + if (prop_crtc_x) + drm_object_attach_property(obj, prop_crtc_x, 0); + if (prop_crtc_y) + drm_object_attach_property(obj, prop_crtc_y, 0); + if (prop_crtc_w) + drm_object_attach_property(obj, prop_crtc_w, 0); + if (prop_crtc_h) + drm_object_attach_property(obj, prop_crtc_h, 0); + + if (prop_fb_id) + drm_object_attach_property(obj, prop_fb_id, 0); + if (prop_crtc_id) + drm_object_attach_property(obj, prop_crtc_id, 0); + } + + list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { + struct drm_mode_object *obj = &crtc->base; + + if (prop_src_x) + drm_object_attach_property(obj, prop_src_x, 0); + if (prop_src_y) + drm_object_attach_property(obj, prop_src_y, 0); + + if (prop_fb_id) + drm_object_attach_property(obj, prop_fb_id, 0); + if (prop_mode_id) + drm_object_attach_property(obj, prop_mode_id, 0); + if (prop_connector_ids) + drm_object_attach_property(obj, prop_connector_ids, 0); + } + + dev->driver->atomic_funcs = &intel_atomic_funcs; + + /* quick way to pre-populate the properties... */ + state = intel_atomic_begin(dev, 0); + if (state) { + update_props(state); + intel_atomic_end(dev, state); + } + + return 0; +} + +void intel_atomic_fini(struct drm_device *dev) +{ + kfree(connector_ids); + + if (blob_connector_ids) + drm_property_destroy_blob(dev, blob_connector_ids); + + drm_property_destroy(dev, prop_src_x); + drm_property_destroy(dev, prop_src_y); + drm_property_destroy(dev, prop_src_w); + drm_property_destroy(dev, prop_src_h); + + drm_property_destroy(dev, prop_crtc_x); + drm_property_destroy(dev, prop_crtc_y); + drm_property_destroy(dev, prop_crtc_w); + drm_property_destroy(dev, prop_crtc_h); + + drm_property_destroy(dev, prop_fb_id); + drm_property_destroy(dev, prop_crtc_id); + drm_property_destroy(dev, prop_mode_id); + drm_property_destroy(dev, prop_connector_ids); +} diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index aec6cac..3ff6ae9 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -7049,6 +7049,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; @@ -7103,6 +7106,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) @@ -7118,6 +7123,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 _______________________________________________ dri-devel mailing list dri-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/dri-devel