static int set_property_atomic(struct drm_mode_object *obj, struct drm_property *prop, uint64_t prop_value) { struct drm_device *dev = prop->dev; struct drm_atomic_state *state; struct drm_modeset_acquire_ctx ctx; int ret; drm_modeset_acquire_init(&ctx, 0); state = drm_atomic_state_alloc(dev); if (!state) return -ENOMEM; state->acquire_ctx = &ctx; retry: if (prop == state->dev->mode_config.dpms_property) { if (obj->type != DRM_MODE_OBJECT_CONNECTOR) { ret = -EINVAL; goto out; } ret = drm_atomic_connector_commit_dpms(state, obj_to_connector(obj), prop_value); } else { ret = drm_atomic_set_property(state, obj, prop, prop_value); if (ret) goto out; ret = drm_atomic_commit(state); } out: if (ret == -EDEADLK) { drm_atomic_state_clear(state); drm_modeset_backoff(&ctx); goto retry; } drm_atomic_state_put(state); drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); return ret; }
/** * drm_modeset_lock_crtc - lock crtc with hidden acquire ctx for a plane update * @crtc: DRM CRTC * @plane: DRM plane to be updated on @crtc * * This function locks the given crtc and plane (which should be either the * primary or cursor plane) using a hidden acquire context. This is necessary so * that drivers internally using the atomic interfaces can grab further locks * with the lock acquire context. * * Note that @plane can be NULL, e.g. when the cursor support hasn't yet been * converted to universal planes yet. */ void drm_modeset_lock_crtc(struct drm_crtc *crtc, struct drm_plane *plane) { struct drm_modeset_acquire_ctx *ctx; int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (WARN_ON(!ctx)) return; drm_modeset_acquire_init(ctx, 0); retry: ret = drm_modeset_lock(&crtc->mutex, ctx); if (ret) goto fail; if (plane) { ret = drm_modeset_lock(&plane->mutex, ctx); if (ret) goto fail; if (plane->crtc) { ret = drm_modeset_lock(&plane->crtc->mutex, ctx); if (ret) goto fail; } } WARN_ON(crtc->acquire_ctx); /* now we hold the locks, so now that it is safe, stash the * ctx for drm_modeset_unlock_crtc(): */ crtc->acquire_ctx = ctx; return; fail: if (ret == -EDEADLK) { drm_modeset_backoff(ctx); goto retry; } }
static int setplane_internal(struct drm_plane *plane, struct drm_crtc *crtc, struct drm_framebuffer *fb, int32_t crtc_x, int32_t crtc_y, uint32_t crtc_w, uint32_t crtc_h, /* src_{x,y,w,h} values are 16.16 fixed point */ uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h) { struct drm_modeset_acquire_ctx ctx; int ret; drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); retry: ret = drm_modeset_lock_all_ctx(plane->dev, &ctx); if (ret) goto fail; if (drm_drv_uses_atomic_modeset(plane->dev)) ret = __setplane_atomic(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, &ctx); else ret = __setplane_internal(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, &ctx); fail: if (ret == -EDEADLK) { ret = drm_modeset_backoff(&ctx); if (!ret) goto retry; } drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); return ret; }
/** * drm_modeset_lock_all - take all modeset locks * @dev: DRM device * * This function takes all modeset locks, suitable where a more fine-grained * scheme isn't (yet) implemented. Locks must be dropped by calling the * drm_modeset_unlock_all() function. * * This function is deprecated. It allocates a lock acquisition context and * stores it in the DRM device's ->mode_config. This facilitate conversion of * existing code because it removes the need to manually deal with the * acquisition context, but it is also brittle because the context is global * and care must be taken not to nest calls. New code should use the * drm_modeset_lock_all_ctx() function and pass in the context explicitly. */ void drm_modeset_lock_all(struct drm_device *dev) { struct drm_mode_config *config = &dev->mode_config; struct drm_modeset_acquire_ctx *ctx; int ret; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (WARN_ON(!ctx)) return; mutex_lock(&config->mutex); drm_modeset_acquire_init(ctx, 0); retry: ret = drm_modeset_lock_all_ctx(dev, ctx); if (ret < 0) { if (ret == -EDEADLK) { drm_modeset_backoff(ctx); goto retry; } drm_modeset_acquire_fini(ctx); kfree(ctx); return; } WARN_ON(config->acquire_ctx); /* * We hold the locks now, so it is safe to stash the acquisition * context for drm_modeset_unlock_all(). */ config->acquire_ctx = ctx; drm_warn_on_modeset_not_all_locked(dev); }
/* * Try to read the BIOS display configuration and use it for the initial * fb configuration. * * The BIOS or boot loader will generally create an initial display * configuration for us that includes some set of active pipes and displays. * This routine tries to figure out which pipes and connectors are active * and stuffs them into the crtcs and modes array given to us by the * drm_fb_helper code. * * The overall sequence is: * intel_fbdev_init - from driver load * intel_fbdev_init_bios - initialize the intel_fbdev using BIOS data * drm_fb_helper_init - build fb helper structs * drm_fb_helper_single_add_all_connectors - more fb helper structs * intel_fbdev_initial_config - apply the config * drm_fb_helper_initial_config - call ->probe then register_framebuffer() * drm_setup_crtcs - build crtc config for fbdev * intel_fb_initial_config - find active connectors etc * drm_fb_helper_single_fb_probe - set up fbdev * intelfb_create - re-use or alloc fb, build out fbdev structs * * Note that we don't make special consideration whether we could actually * switch to the selected modes without a full modeset. E.g. when the display * is in VGA mode we need to recalculate watermarks and set a new high-res * framebuffer anyway. */ static bool intel_fb_initial_config(struct drm_fb_helper *fb_helper, struct drm_fb_helper_crtc **crtcs, struct drm_display_mode **modes, struct drm_fb_offset *offsets, bool *enabled, int width, int height) { struct drm_i915_private *dev_priv = to_i915(fb_helper->dev); unsigned long conn_configured, conn_seq, mask; unsigned int count = min(fb_helper->connector_count, BITS_PER_LONG); int i, j; bool *save_enabled; bool fallback = true, ret = true; int num_connectors_enabled = 0; int num_connectors_detected = 0; struct drm_modeset_acquire_ctx ctx; save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); if (!save_enabled) return false; drm_modeset_acquire_init(&ctx, 0); while (drm_modeset_lock_all_ctx(fb_helper->dev, &ctx) != 0) drm_modeset_backoff(&ctx); memcpy(save_enabled, enabled, count); mask = GENMASK(count - 1, 0); conn_configured = 0; retry: conn_seq = conn_configured; for (i = 0; i < count; i++) { struct drm_fb_helper_connector *fb_conn; struct drm_connector *connector; struct drm_encoder *encoder; struct drm_fb_helper_crtc *new_crtc; fb_conn = fb_helper->connector_info[i]; connector = fb_conn->connector; if (conn_configured & BIT(i)) continue; if (conn_seq == 0 && !connector->has_tile) continue; if (connector->status == connector_status_connected) num_connectors_detected++; if (!enabled[i]) { DRM_DEBUG_KMS("connector %s not enabled, skipping\n", connector->name); conn_configured |= BIT(i); continue; } if (connector->force == DRM_FORCE_OFF) { DRM_DEBUG_KMS("connector %s is disabled by user, skipping\n", connector->name); enabled[i] = false; continue; } encoder = connector->state->best_encoder; if (!encoder || WARN_ON(!connector->state->crtc)) { if (connector->force > DRM_FORCE_OFF) goto bail; DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", connector->name); enabled[i] = false; conn_configured |= BIT(i); continue; } num_connectors_enabled++; new_crtc = intel_fb_helper_crtc(fb_helper, connector->state->crtc); /* * Make sure we're not trying to drive multiple connectors * with a single CRTC, since our cloning support may not * match the BIOS. */ for (j = 0; j < count; j++) { if (crtcs[j] == new_crtc) { DRM_DEBUG_KMS("fallback: cloned configuration\n"); goto bail; } } DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", connector->name); /* go for command line mode first */ modes[i] = drm_pick_cmdline_mode(fb_conn); /* try for preferred next */ if (!modes[i]) { DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", connector->name, connector->has_tile); modes[i] = drm_has_preferred_mode(fb_conn, width, height); } /* No preferred mode marked by the EDID? Are there any modes? */ if (!modes[i] && !list_empty(&connector->modes)) { DRM_DEBUG_KMS("using first mode listed on connector %s\n", connector->name); modes[i] = list_first_entry(&connector->modes, struct drm_display_mode, head); } /* last resort: use current mode */ if (!modes[i]) { /* * IMPORTANT: We want to use the adjusted mode (i.e. * after the panel fitter upscaling) as the initial * config, not the input mode, which is what crtc->mode * usually contains. But since our current * code puts a mode derived from the post-pfit timings * into crtc->mode this works out correctly. * * This is crtc->mode and not crtc->state->mode for the * fastboot check to work correctly. crtc_state->mode has * I915_MODE_FLAG_INHERITED, which we clear to force check * state. */ DRM_DEBUG_KMS("looking for current mode on connector %s\n", connector->name); modes[i] = &connector->state->crtc->mode; } crtcs[i] = new_crtc; DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", connector->name, connector->state->crtc->base.id, connector->state->crtc->name, modes[i]->hdisplay, modes[i]->vdisplay, modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" :""); fallback = false; conn_configured |= BIT(i); }
static void hsw_pipe_A_crc_wa(struct drm_i915_private *dev_priv, bool enable) { struct drm_device *dev = &dev_priv->drm; struct intel_crtc *crtc = intel_get_crtc_for_pipe(dev_priv, PIPE_A); struct intel_crtc_state *pipe_config; struct drm_atomic_state *state; struct drm_modeset_acquire_ctx ctx; int ret = 0; drm_modeset_acquire_init(&ctx, 0); state = drm_atomic_state_alloc(dev); if (!state) { ret = -ENOMEM; goto unlock; } state->acquire_ctx = &ctx; retry: pipe_config = intel_atomic_get_crtc_state(state, crtc); if (IS_ERR(pipe_config)) { ret = PTR_ERR(pipe_config); goto put_state; } if (HAS_IPS(dev_priv)) { /* * When IPS gets enabled, the pipe CRC changes. Since IPS gets * enabled and disabled dynamically based on package C states, * user space can't make reliable use of the CRCs, so let's just * completely disable it. */ pipe_config->ips_force_disable = enable; if (pipe_config->ips_enabled == enable) pipe_config->base.connectors_changed = true; } if (IS_HASWELL(dev_priv)) { pipe_config->pch_pfit.force_thru = enable; if (pipe_config->cpu_transcoder == TRANSCODER_EDP && pipe_config->pch_pfit.enabled != enable) pipe_config->base.connectors_changed = true; } ret = drm_atomic_commit(state); put_state: if (ret == -EDEADLK) { drm_atomic_state_clear(state); drm_modeset_backoff(&ctx); goto retry; } drm_atomic_state_put(state); unlock: WARN(ret, "Toggling workaround to %i returns %i\n", enable, ret); drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); }
static int drm_mode_cursor_universal(struct drm_crtc *crtc, struct drm_mode_cursor2 *req, struct drm_file *file_priv, struct drm_modeset_acquire_ctx *ctx) { struct drm_device *dev = crtc->dev; struct drm_plane *plane = crtc->cursor; struct drm_framebuffer *fb = NULL; struct drm_mode_fb_cmd2 fbreq = { .width = req->width, .height = req->height, .pixel_format = DRM_FORMAT_ARGB8888, .pitches = { req->width * 4 }, .handles = { req->handle }, }; int32_t crtc_x, crtc_y; uint32_t crtc_w = 0, crtc_h = 0; uint32_t src_w = 0, src_h = 0; int ret = 0; BUG_ON(!plane); WARN_ON(plane->crtc != crtc && plane->crtc != NULL); /* * Obtain fb we'll be using (either new or existing) and take an extra * reference to it if fb != null. setplane will take care of dropping * the reference if the plane update fails. */ if (req->flags & DRM_MODE_CURSOR_BO) { if (req->handle) { fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv); if (IS_ERR(fb)) { DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); return PTR_ERR(fb); } fb->hot_x = req->hot_x; fb->hot_y = req->hot_y; } else { fb = NULL; } } else { if (plane->state) fb = plane->state->fb; else fb = plane->fb; if (fb) drm_framebuffer_get(fb); } if (req->flags & DRM_MODE_CURSOR_MOVE) { crtc_x = req->x; crtc_y = req->y; } else { crtc_x = crtc->cursor_x; crtc_y = crtc->cursor_y; } if (fb) { crtc_w = fb->width; crtc_h = fb->height; src_w = fb->width << 16; src_h = fb->height << 16; } if (drm_drv_uses_atomic_modeset(dev)) ret = __setplane_atomic(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, 0, 0, src_w, src_h, ctx); else ret = __setplane_internal(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, 0, 0, src_w, src_h, ctx); if (fb) drm_framebuffer_put(fb); /* Update successful; save new cursor position, if necessary */ if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { crtc->cursor_x = req->x; crtc->cursor_y = req->y; } return ret; } static int drm_mode_cursor_common(struct drm_device *dev, struct drm_mode_cursor2 *req, struct drm_file *file_priv) { struct drm_crtc *crtc; struct drm_modeset_acquire_ctx ctx; int ret = 0; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) return -EINVAL; crtc = drm_crtc_find(dev, file_priv, req->crtc_id); if (!crtc) { DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); return -ENOENT; } drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); retry: ret = drm_modeset_lock(&crtc->mutex, &ctx); if (ret) goto out; /* * If this crtc has a universal cursor plane, call that plane's update * handler rather than using legacy cursor handlers. */ if (crtc->cursor) { ret = drm_modeset_lock(&crtc->cursor->mutex, &ctx); if (ret) goto out; ret = drm_mode_cursor_universal(crtc, req, file_priv, &ctx); goto out; } if (req->flags & DRM_MODE_CURSOR_BO) { if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { ret = -ENXIO; goto out; } /* Turns off the cursor if handle is 0 */ if (crtc->funcs->cursor_set2) ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, req->width, req->height, req->hot_x, req->hot_y); else ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, req->width, req->height); } if (req->flags & DRM_MODE_CURSOR_MOVE) { if (crtc->funcs->cursor_move) { ret = crtc->funcs->cursor_move(crtc, req->x, req->y); } else { ret = -EFAULT; goto out; } } out: if (ret == -EDEADLK) { ret = drm_modeset_backoff(&ctx); if (!ret) goto retry; } drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); return ret; } int drm_mode_cursor_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_cursor *req = data; struct drm_mode_cursor2 new_req; memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); new_req.hot_x = new_req.hot_y = 0; return drm_mode_cursor_common(dev, &new_req, file_priv); } /* * Set the cursor configuration based on user request. This implements the 2nd * version of the cursor ioctl, which allows userspace to additionally specify * the hotspot of the pointer. */ int drm_mode_cursor2_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_cursor2 *req = data; return drm_mode_cursor_common(dev, req, file_priv); } int drm_mode_page_flip_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_crtc_page_flip_target *page_flip = data; struct drm_crtc *crtc; struct drm_plane *plane; struct drm_framebuffer *fb = NULL, *old_fb; struct drm_pending_vblank_event *e = NULL; u32 target_vblank = page_flip->sequence; struct drm_modeset_acquire_ctx ctx; int ret = -EINVAL; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS) return -EINVAL; if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) return -EINVAL; /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags * can be specified */ if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET) return -EINVAL; if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) return -EINVAL; crtc = drm_crtc_find(dev, file_priv, page_flip->crtc_id); if (!crtc) return -ENOENT; plane = crtc->primary; if (crtc->funcs->page_flip_target) { u32 current_vblank; int r; r = drm_crtc_vblank_get(crtc); if (r) return r; current_vblank = (u32)drm_crtc_vblank_count(crtc); switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: if ((int)(target_vblank - current_vblank) > 1) { DRM_DEBUG("Invalid absolute flip target %u, " "must be <= %u\n", target_vblank, current_vblank + 1); drm_crtc_vblank_put(crtc); return -EINVAL; } break; case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: if (target_vblank != 0 && target_vblank != 1) { DRM_DEBUG("Invalid relative flip target %u, " "must be 0 or 1\n", target_vblank); drm_crtc_vblank_put(crtc); return -EINVAL; } target_vblank += current_vblank; break; default: target_vblank = current_vblank + !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); break; } } else if (crtc->funcs->page_flip == NULL || (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { return -EINVAL; } drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE); retry: ret = drm_modeset_lock(&crtc->mutex, &ctx); if (ret) goto out; ret = drm_modeset_lock(&plane->mutex, &ctx); if (ret) goto out; if (plane->state) old_fb = plane->state->fb; else old_fb = plane->fb; if (old_fb == NULL) { /* The framebuffer is currently unbound, presumably * due to a hotplug event, that userspace has not * yet discovered. */ ret = -EBUSY; goto out; } fb = drm_framebuffer_lookup(dev, file_priv, page_flip->fb_id); if (!fb) { ret = -ENOENT; goto out; } if (plane->state) { const struct drm_plane_state *state = plane->state; ret = drm_framebuffer_check_src_coords(state->src_x, state->src_y, state->src_w, state->src_h, fb); } else { ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); } if (ret) goto out; if (old_fb->format != fb->format) { DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); ret = -EINVAL; goto out; } if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { e = kzalloc(sizeof *e, GFP_KERNEL); if (!e) { ret = -ENOMEM; goto out; } e->event.base.type = DRM_EVENT_FLIP_COMPLETE; e->event.base.length = sizeof(e->event); e->event.vbl.user_data = page_flip->user_data; e->event.vbl.crtc_id = crtc->base.id; ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); if (ret) { kfree(e); e = NULL; goto out; } } plane->old_fb = plane->fb; if (crtc->funcs->page_flip_target) ret = crtc->funcs->page_flip_target(crtc, fb, e, page_flip->flags, target_vblank, &ctx); else ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags, &ctx); if (ret) { if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) drm_event_cancel_free(dev, &e->base); /* Keep the old fb, don't unref it. */ plane->old_fb = NULL; } else { if (!plane->state) { plane->fb = fb; drm_framebuffer_get(fb); } } out: if (fb) drm_framebuffer_put(fb); if (plane->old_fb) drm_framebuffer_put(plane->old_fb); plane->old_fb = NULL; if (ret == -EDEADLK) { ret = drm_modeset_backoff(&ctx); if (!ret) goto retry; } drm_modeset_drop_locks(&ctx); drm_modeset_acquire_fini(&ctx); if (ret && crtc->funcs->page_flip_target) drm_crtc_vblank_put(crtc); return ret; }