From patchwork Wed Oct 10 15:04:58 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?b?VmlsbGUgU3lyasOkbMOk?= X-Patchwork-Id: 1574691 Return-Path: X-Original-To: patchwork-dri-devel@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by patchwork1.kernel.org (Postfix) with ESMTP id 5EAEC4025F for ; Wed, 10 Oct 2012 15:26:40 +0000 (UTC) Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 244BBA028D for ; Wed, 10 Oct 2012 08:26:40 -0700 (PDT) X-Original-To: dri-devel@lists.freedesktop.org Delivered-To: dri-devel@lists.freedesktop.org Received: from mga02.intel.com (mga02.intel.com [134.134.136.20]) by gabe.freedesktop.org (Postfix) with ESMTP id CBC75A08D6 for ; Wed, 10 Oct 2012 08:05:14 -0700 (PDT) Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 10 Oct 2012 08:05:14 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.80,564,1344236400"; d="scan'208";a="203913278" Received: from stinkbox.fi.intel.com (HELO stinkbox) ([10.237.72.168]) by orsmga001.jf.intel.com with SMTP; 10 Oct 2012 08:05:11 -0700 Received: by stinkbox (sSMTP sendmail emulation); Wed, 10 Oct 2012 18:05:10 +0300 From: ville.syrjala@linux.intel.com To: dri-devel@lists.freedesktop.org Subject: [RFC PATCH] drm/i915: Implement atomic modesetting Date: Wed, 10 Oct 2012 18:04:58 +0300 Message-Id: <1349881499-5711-3-git-send-email-ville.syrjala@linux.intel.com> X-Mailer: git-send-email 1.7.8.6 In-Reply-To: <1349881499-5711-1-git-send-email-ville.syrjala@linux.intel.com> References: <1349881499-5711-1-git-send-email-ville.syrjala@linux.intel.com> MIME-Version: 1.0 X-BeenThere: dri-devel@lists.freedesktop.org X-Mailman-Version: 2.1.13 Precedence: list List-Id: Direct Rendering Infrastructure - Development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org Errors-To: dri-devel-bounces+patchwork-dri-devel=patchwork.kernel.org@lists.freedesktop.org From: Ville Syrjälä Implement the atomic modeset operations. TODO: need to rewrite this for the new intel modeset code Signed-off-by: Ville Syrjälä --- drivers/gpu/drm/i915/Makefile | 1 + drivers/gpu/drm/i915/intel_atomic.c | 1462 ++++++++++++++++++++++++++++++++++ drivers/gpu/drm/i915/intel_display.c | 7 + 3 files changed, 1470 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 b0bacdb..377d100 100644 --- a/drivers/gpu/drm/i915/Makefile +++ b/drivers/gpu/drm/i915/Makefile @@ -16,6 +16,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..363018f --- /dev/null +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -0,0 +1,1462 @@ +/* + */ + +#include +#include + +#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; +static struct drm_property *prop_connector_ids; +static struct drm_property *prop_cursor_id; +static struct drm_property *prop_cursor_x; +static struct drm_property *prop_cursor_y; +static struct drm_property *prop_cursor_w; +static struct drm_property *prop_cursor_h; + +struct intel_plane_state { + struct drm_plane *plane; + struct drm_framebuffer *old_fb; + struct intel_plane_coords coords; + bool dirty; + bool pinned; +}; + +struct intel_crtc_state { + struct drm_crtc *crtc; + struct drm_framebuffer *old_fb; + struct drm_i915_gem_object *old_cursor_bo; + bool mode_dirty; + bool fb_dirty; + bool cursor_dirty; + bool active_dirty; + bool pinned; + bool cursor_pinned; + unsigned long connectors_bitmask; + unsigned long encoders_bitmask; +}; + +struct intel_atomic_state { + struct drm_file *file; + struct intel_plane_state *plane; + struct intel_crtc_state *crtc; + bool dirty; + bool restore_hw; + unsigned int flags; + uint64_t user_data; + struct drm_plane *saved_planes; + struct intel_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, struct drm_file *file, + uint32_t flags, uint64_t user_data) +{ + 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->flags = flags; + state->file = file; + state->user_data = user_data; + + 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 intel_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++]; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + s->crtc = crtc; + s->old_fb = crtc->fb; + s->old_cursor_bo = intel_crtc->cursor_bo; + + 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; + s->old_fb = plane->fb; + } + + 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) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + state->saved_crtcs[i++] = *intel_crtc; + } + i = 0; + list_for_each_entry(plane, &dev->mode_config.plane_list, head) + state->saved_planes[i++] = *plane; + + state->file = file; + + 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; + struct intel_crtc *intel_crtc = to_intel_crtc(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) { + struct drm_mode_modeinfo *umode = blob_data; + struct drm_display_mode *mode = NULL; + + if (value != 0 && value != sizeof(*umode)) { + DRM_DEBUG_KMS("Invalid mode length\n"); + return -EINVAL; + } + + if (!crtc->enabled) { + if (value == 0) + return 0; + } + + if (value) { + int ret; + + mode = drm_mode_create(crtc->dev); + if (!mode) + return -ENOMEM; + + ret = drm_crtc_convert_umode(mode, umode); + if (ret) { + DRM_DEBUG_KMS("Invalid mode\n"); + drm_mode_debug_printmodeline(mode); + drm_mode_destroy(crtc->dev, mode); + return ret; + } + + drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); + } + + if (crtc->enabled && mode && drm_mode_equal(&crtc->mode, mode)) { + drm_mode_destroy(crtc->dev, 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; + drm_mode_destroy(crtc->dev, mode); + } 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 if (prop == prop_cursor_id) { + if (intel_crtc->cursor_handle == value) + return 0; + intel_crtc->cursor_handle = value; + state->cursor_dirty = true; + } else if (prop == prop_cursor_x) { + if (intel_crtc->cursor_x == value) + return 0; + intel_crtc->cursor_x = value; + state->cursor_dirty = true; + } else if (prop == prop_cursor_y) { + if (intel_crtc->cursor_y == value) + return 0; + intel_crtc->cursor_y = value; + state->cursor_dirty = true; + } else if (prop == prop_cursor_w) { + if (value != 0 && value != 64) + return -EINVAL; + if (intel_crtc->cursor_width == value) + return 0; + intel_crtc->cursor_width = value; + state->cursor_dirty = true; + } else if (prop == prop_cursor_h) { + if (value != 0 && value != 64) + return -EINVAL; + if (intel_crtc->cursor_height == value) + return 0; + intel_crtc->cursor_height = value; + state->cursor_dirty = true; + } 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; + + if (!crtc->enabled) + return; + + 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) +{ + const struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private; + + return crtc_funcs->mode_set_base_nopin(crtc, crtc->x, crtc->y); +} + +static int crtc_mode_set(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; + int ret; + + if (!crtc->enabled) { + crtc_funcs->disable_nopin(crtc); + return 0; + } + + ret = crtc_funcs->mode_set_nopin(crtc, &crtc->mode, &crtc->hwmode, crtc->x, crtc->y); + 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; + + if (!crtc->enabled) + return; + + 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, + bool pin); + +static void unpin_cursors(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]; + struct drm_crtc *crtc = st->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!st->cursor_pinned) + continue; + + intel_crtc_cursor_bo_unref(crtc, intel_crtc->cursor_bo); + + st->cursor_pinned = false; + } +} + +static int pin_cursors(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i, ret; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + if (!st->cursor_dirty) + continue; + + ret = intel_crtc_cursor_prepare(crtc, s->file, + intel_crtc->cursor_handle, + intel_crtc->cursor_width, + intel_crtc->cursor_height, + &intel_crtc->cursor_bo, + &intel_crtc->cursor_addr); + if (ret) + goto unpin; + + st->cursor_pinned = true; + } + + return 0; + +unpin: + unpin_cursors(dev, s); + + return ret; +} + +static void unpin_old_cursors(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]; + struct drm_crtc *crtc = st->crtc; + + if (!st->cursor_dirty) + continue; + + if (!st->old_cursor_bo) + continue; + + intel_crtc_cursor_bo_unref(crtc, st->old_cursor_bo); + } +} + +static void unpin_fbs(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = dev->mode_config.num_plane - 1; i >= 0; i--) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct drm_i915_gem_object *obj; + + if (!st->pinned) + continue; + + obj = to_intel_framebuffer(plane->fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + + st->pinned = false; + } + + for (i = dev->mode_config.num_crtc - 1; i >= 0; i--) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct drm_i915_gem_object *obj; + + if (!st->pinned) + continue; + + obj = to_intel_framebuffer(crtc->fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + + st->pinned = false; + } +} + +static int pin_fbs(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i, ret; + + for (i = 0; i < dev->mode_config.num_crtc; i++) { + struct intel_crtc_state *st = &s->crtc[i]; + struct drm_crtc *crtc = st->crtc; + struct drm_i915_gem_object *obj; + + if (!st->fb_dirty) + continue; + + if (!crtc->fb) + continue; + + obj = to_intel_framebuffer(crtc->fb)->obj; + + mutex_lock(&dev->struct_mutex); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto unpin; + + st->pinned = true; + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct drm_i915_gem_object *obj; + + if (!st->dirty) + continue; + + if (!plane->fb) + continue; + + obj = to_intel_framebuffer(plane->fb)->obj; + + mutex_lock(&dev->struct_mutex); + ret = intel_pin_and_fence_fb_obj(dev, obj, NULL); + mutex_unlock(&dev->struct_mutex); + + if (ret) + goto unpin; + + st->pinned = true; + } + + return 0; + + unpin: + unpin_fbs(dev, s); + + return ret; +} + +static void unpin_old_fbs(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]; + struct drm_i915_gem_object *obj; + + if (!st->fb_dirty) + continue; + + if (!st->old_fb) + continue; + + obj = to_intel_framebuffer(st->old_fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + } + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_i915_gem_object *obj; + + if (!st->dirty) + continue; + + if (!st->old_fb) + continue; + + obj = to_intel_framebuffer(st->old_fb)->obj; + + mutex_lock(&dev->struct_mutex); + intel_unpin_fb_obj(obj); + mutex_unlock(&dev->struct_mutex); + } +} + +static void update_plane_obj(struct drm_device *dev, + struct intel_atomic_state *s) +{ + int i; + + for (i = 0; i < dev->mode_config.num_plane; i++) { + struct intel_plane_state *st = &s->plane[i]; + struct drm_plane *plane = st->plane; + struct intel_plane *intel_plane = to_intel_plane(plane); + + if (!st->dirty) + continue; + + if (plane->fb) + intel_plane->obj = to_intel_framebuffer(plane->fb)->obj; + else + intel_plane->obj = NULL; + } +} + +void _intel_disable_plane(struct drm_plane *plane, bool unpin); + +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]; + + mutex_lock(&dev->struct_mutex); + + if (st->mode_dirty) { + /* wait for pending MI_WAIT_FOR_EVENTs */ + if (st->old_fb) + intel_finish_fb(st->old_fb); + } + + if (st->fb_dirty) { + /* wait for pending page flips */ + if (st->crtc->fb) + intel_finish_fb(st->crtc->fb); + } + + mutex_unlock(&dev->struct_mutex); + + 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]; + struct intel_crtc *intel_crtc = to_intel_crtc(st->crtc); + int j; + + if (st->mode_dirty) { + ret = crtc_mode_set(st->crtc); + if (ret) + return ret; + } else if (st->fb_dirty) { + ret = crtc_set_base(st->crtc); + if (ret) + return ret; + } + + if (st->cursor_dirty) + intel_crtc_cursor_commit(st->crtc, + intel_crtc->cursor_handle, + intel_crtc->cursor_width, + intel_crtc->cursor_height, + intel_crtc->cursor_bo, + intel_crtc->cursor_addr); + + for (j = 0; j < dev->mode_config.num_plane; j++) { + struct intel_plane_state *pst = &s->plane[j]; + struct drm_plane *plane = pst->plane; + + if (!pst->dirty) + continue; + + if (plane->crtc != st->crtc) + continue; + + ret = intel_commit_plane(plane, plane->crtc, plane->fb, &pst->coords, false); + 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) + _intel_disable_plane(plane, false); + } + + /* 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) { + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + *intel_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) +{ + struct drm_crtc *crtc = s->crtc; + struct intel_crtc *intel_crtc = to_intel_crtc(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; + int ret; + + /* 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 (intel_crtc->cursor_visible && + (intel_crtc->cursor_width != 64 || + intel_crtc->cursor_height != 64)) { + DRM_DEBUG_KMS("only 64x64 cursor sprites are supported\n"); + return -EINVAL; + } + + if (!crtc->enabled || !s->mode_dirty) + return 0; + + mode = adjusted_mode = crtc->mode; + + ret = intel_check_clock(dev, crtc, mode.clock); + 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_funcs->mode_fixup(encoder, &mode, &adjusted_mode)) + return -EINVAL; + } + + if (!crtc_funcs->mode_fixup(crtc, &mode, &adjusted_mode)) + return -EINVAL; + + 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 && !st->cursor_dirty) + continue; + + if (st->mode_dirty && s->flags & DRM_MODE_ATOMIC_NONBLOCK) + return -EAGAIN; + + ret = check_crtc(st); + 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. + * + * Also any modesetting will disable+enable the pipe, so the + * plane needs to be re-enabled afterwards too. + * TODO: there's no need to redo the clipping in such cases + * if the computed values were cached, the could be commited + * directly. + */ + if (st->active_dirty || st->mode_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 update_connector_ids(struct drm_crtc *crtc) +{ + struct drm_device *dev = crtc->dev; + struct drm_connector *connector; + 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) { + drm_property_blob_replace_data(crtc->connector_ids_blob, + i * sizeof connector_ids[0], connector_ids); + value = crtc->connector_ids_blob->base.id; + } else + drm_property_blob_replace_data(crtc->connector_ids_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_connector_ids, value); + + return 0; +} + +static int update_mode(struct drm_crtc *crtc) +{ + uint64_t value = 0; + + if (crtc->enabled) { + struct drm_mode_modeinfo umode; + + drm_crtc_convert_to_umode(&umode, &crtc->mode); + drm_property_blob_replace_data(crtc->mode_blob, sizeof umode, &umode); + value = crtc->mode_blob->base.id; + } else + drm_property_blob_replace_data(crtc->mode_blob, 0, NULL); + + drm_object_property_set_value(&crtc->base, prop_mode, value); + + return 0; +} + +static void update_crtc_props(struct drm_crtc *crtc) +{ + struct drm_mode_object *obj = &crtc->base; + struct intel_crtc *intel_crtc = to_intel_crtc(crtc); + + 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); + + drm_object_property_set_value(obj, prop_cursor_id, + intel_crtc->cursor_handle); + drm_object_property_set_value(obj, prop_cursor_x, intel_crtc->cursor_x); + drm_object_property_set_value(obj, prop_cursor_y, intel_crtc->cursor_y); + drm_object_property_set_value(obj, prop_cursor_w, + intel_crtc->cursor_width); + drm_object_property_set_value(obj, prop_cursor_h, + intel_crtc->cursor_height); + + update_mode(crtc); + update_connector_ids(crtc); +} + +static void update_props(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->flags & DRM_MODE_ATOMIC_NONBLOCK) + return -ENOSYS; + + if (!s->dirty) + return 0; + + ret = pin_fbs(dev, s); + if (ret) + return ret; + + ret = pin_cursors(dev, s); + if (ret) + return ret; + + /* apply in a blocking manner */ + ret = apply_config(dev, s); + if (ret) { + unpin_cursors(dev, s); + unpin_fbs(dev, s); + s->restore_hw = true; + return ret; + } + + unpin_old_cursors(dev, s); + unpin_old_fbs(dev, s); + + update_plane_obj(dev, s); + + 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, +}; + +static struct { + struct drm_property **prop; + const char *name; + uint64_t min; + uint64_t max; +} props[] = { + { &prop_src_x, "SRC_X", 0, UINT_MAX }, + { &prop_src_y, "SRC_Y", 0, UINT_MAX }, + { &prop_src_w, "SRC_W", 0, UINT_MAX }, + { &prop_src_h, "SRC_H", 0, UINT_MAX }, + + { &prop_crtc_x, "CRTC_X", INT_MIN, INT_MAX }, + { &prop_crtc_y, "CRTC_Y", INT_MIN, INT_MAX }, + { &prop_crtc_w, "CRTC_W", 0, INT_MAX }, + { &prop_crtc_h, "CRTC_H", 0, INT_MAX }, + + { &prop_fb_id, "FB_ID", 0, UINT_MAX }, + { &prop_crtc_id, "CRTC_ID", 0, UINT_MAX }, + + { &prop_cursor_id, "CURSOR_ID", 0, UINT_MAX }, + { &prop_cursor_w, "CURSOR_W", 0, UINT_MAX }, + { &prop_cursor_h, "CURSOR_H", 0, UINT_MAX }, + { &prop_cursor_x, "CURSOR_X", INT_MIN, INT_MAX }, + { &prop_cursor_y, "CURSOR_Y", INT_MIN, INT_MAX }, +}; + +int intel_atomic_init(struct drm_device *dev) +{ + struct drm_crtc *crtc; + struct drm_plane *plane; + int ret = -ENOMEM; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(props); i++) { + *props[i].prop = + drm_property_create_range(dev, 0, props[i].name, + props[i].min, props[i].max); + if (!*props[i].prop) + goto out; + } + + /* 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 out; + + prop_mode = drm_property_create(dev, DRM_MODE_PROP_BLOB, "MODE", 0); + if (!prop_mode) + goto out; + + 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, 0); + drm_object_attach_property(obj, prop_connector_ids, 0); + + drm_object_attach_property(obj, prop_cursor_id, 0); + drm_object_attach_property(obj, prop_cursor_x, 0); + drm_object_attach_property(obj, prop_cursor_y, 0); + drm_object_attach_property(obj, prop_cursor_w, 0); + drm_object_attach_property(obj, prop_cursor_h, 0); + + crtc->mode_blob = drm_property_create_blob(dev, 0, sizeof(struct drm_mode_modeinfo), NULL); + if (!crtc->mode_blob) + goto out; + + crtc->connector_ids_blob = drm_property_create_blob(dev, 0, + dev->mode_config.num_connector * sizeof(uint32_t), NULL); + if (!crtc->connector_ids_blob) + goto out; + + update_crtc_props(crtc); + } + + dev->driver->atomic_funcs = &intel_atomic_funcs; + + return 0; + + out: + drm_property_destroy(dev, prop_mode); + drm_property_destroy(dev, prop_connector_ids); + + while (i--) + drm_property_destroy(dev, *props[i].prop); + + 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) { + drm_property_destroy_blob(dev, crtc->mode_blob); + drm_property_destroy_blob(dev, crtc->connector_ids_blob); + } + + drm_property_destroy(dev, prop_connector_ids); + drm_property_destroy(dev, prop_mode); + 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 592065e..d6a3e51 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -43,6 +43,9 @@ #define HAS_eDP (intel_pipe_has_type(crtc, INTEL_OUTPUT_EDP)) +void intel_atomic_init(struct drm_device *dev); +void intel_atomic_fini(struct drm_device *dev); + bool intel_pipe_has_type(struct drm_crtc *crtc, int type); static void intel_increase_pllclock(struct drm_crtc *crtc); static void intel_crtc_update_cursor(struct drm_crtc *crtc, bool on); @@ -7014,6 +7017,8 @@ static void intel_setup_outputs(struct drm_device *dev) intel_encoder_clones(dev, encoder->clone_mask); } + intel_atomic_init(dev); + /* disable all the possible outputs/crtcs before entering KMS mode */ drm_helper_disable_unused_functions(dev); @@ -7484,6 +7489,8 @@ void intel_modeset_cleanup(struct drm_device *dev) del_timer_sync(&dev_priv->idle_timer); cancel_work_sync(&dev_priv->idle_work); + intel_atomic_fini(dev); + drm_mode_config_cleanup(dev); }