int drm_mode_setplane(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct drm_mode_set_plane *plane_req = data; struct drm_plane *plane; struct drm_crtc *crtc = NULL; struct drm_framebuffer *fb = NULL; int ret; if (!drm_core_check_feature(dev, DRIVER_MODESET)) return -EOPNOTSUPP; /* * First, find the plane, crtc, and fb objects. If not available, * we don't bother to call the driver. */ plane = drm_plane_find(dev, file_priv, plane_req->plane_id); if (!plane) { DRM_DEBUG_KMS("Unknown plane ID %d\n", plane_req->plane_id); return -ENOENT; } if (plane_req->fb_id) { fb = drm_framebuffer_lookup(dev, file_priv, plane_req->fb_id); if (!fb) { DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", plane_req->fb_id); return -ENOENT; } crtc = drm_crtc_find(dev, file_priv, plane_req->crtc_id); if (!crtc) { drm_framebuffer_put(fb); DRM_DEBUG_KMS("Unknown crtc ID %d\n", plane_req->crtc_id); return -ENOENT; } } ret = setplane_internal(plane, crtc, fb, plane_req->crtc_x, plane_req->crtc_y, plane_req->crtc_w, plane_req->crtc_h, plane_req->src_x, plane_req->src_y, plane_req->src_w, plane_req->src_h); if (fb) drm_framebuffer_put(fb); return ret; }
static void hibmc_fbdev_destroy(struct hibmc_fbdev *fbdev) { struct hibmc_framebuffer *gfb = fbdev->fb; struct drm_fb_helper *fbh = &fbdev->helper; drm_fb_helper_unregister_fbi(fbh); drm_fb_helper_fini(fbh); if (gfb) drm_framebuffer_put(&gfb->fb); }
/* * __setplane_internal - setplane handler for internal callers * * This function will take a reference on the new fb for the plane * on success. * * src_{x,y,w,h} are provided in 16.16 fixed point format */ 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 = 0; WARN_ON(drm_drv_uses_atomic_modeset(plane->dev)); /* No fb means shut it down */ if (!fb) { plane->old_fb = plane->fb; ret = plane->funcs->disable_plane(plane, ctx); if (!ret) { plane->crtc = NULL; plane->fb = NULL; } else { plane->old_fb = NULL; } goto out; } ret = __setplane_check(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h); if (ret) goto out; plane->old_fb = plane->fb; ret = plane->funcs->update_plane(plane, crtc, fb, crtc_x, crtc_y, crtc_w, crtc_h, src_x, src_y, src_w, src_h, ctx); if (!ret) { plane->crtc = crtc; plane->fb = fb; drm_framebuffer_get(plane->fb); } else { plane->old_fb = NULL; } out: if (plane->old_fb) drm_framebuffer_put(plane->old_fb); plane->old_fb = NULL; return ret; }
/** * __drm_atomic_helper_plane_destroy_state - release plane state * @state: plane state object to release * * Releases all resources stored in the plane state without actually freeing * the memory of the plane state. This is useful for drivers that subclass the * plane state. */ void __drm_atomic_helper_plane_destroy_state(struct drm_plane_state *state) { if (state->fb) drm_framebuffer_put(state->fb); if (state->fence) dma_fence_put(state->fence); if (state->commit) drm_crtc_commit_put(state->commit); drm_property_blob_put(state->fb_damage_clips); }
void drm_writeback_cleanup_job(struct drm_writeback_job *job) { struct drm_writeback_connector *connector = job->connector; const struct drm_connector_helper_funcs *funcs = connector->base.helper_private; if (job->prepared && funcs->cleanup_writeback_job) funcs->cleanup_writeback_job(connector, job); if (job->fb) drm_framebuffer_put(job->fb); kfree(job); }
static void vkms_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *old_state) { struct vkms_plane_state *vkms_state = to_vkms_plane_state(old_state); struct drm_crtc *crtc = vkms_state->base.crtc; if (crtc) { /* dropping the reference we acquired in * vkms_primary_plane_update() */ if (drm_framebuffer_read_refcount(&vkms_state->crc_data->fb)) drm_framebuffer_put(&vkms_state->crc_data->fb); } kfree(vkms_state->crc_data); vkms_state->crc_data = NULL; __drm_atomic_helper_plane_destroy_state(old_state); kfree(vkms_state); }
/** * drm_plane_force_disable - Forcibly disable a plane * @plane: plane to disable * * Forces the plane to be disabled. * * Used when the plane's current framebuffer is destroyed, * and when restoring fbdev mode. * * Note that this function is not suitable for atomic drivers, since it doesn't * wire through the lock acquisition context properly and hence can't handle * retries or driver private locks. You probably want to use * drm_atomic_helper_disable_plane() or * drm_atomic_helper_disable_planes_on_crtc() instead. */ void drm_plane_force_disable(struct drm_plane *plane) { int ret; if (!plane->fb) return; WARN_ON(drm_drv_uses_atomic_modeset(plane->dev)); plane->old_fb = plane->fb; ret = plane->funcs->disable_plane(plane, NULL); if (ret) { DRM_ERROR("failed to disable plane with busy fb\n"); plane->old_fb = NULL; return; } /* disconnect the plane from the fb and crtc: */ drm_framebuffer_put(plane->old_fb); plane->old_fb = NULL; plane->fb = NULL; plane->crtc = NULL; }
static int intelfb_create(struct drm_fb_helper *helper, struct drm_fb_helper_surface_size *sizes) { struct intel_fbdev *ifbdev = container_of(helper, struct intel_fbdev, helper); struct intel_framebuffer *intel_fb = ifbdev->fb; struct drm_device *dev = helper->dev; struct drm_i915_private *dev_priv = to_i915(dev); struct pci_dev *pdev = dev_priv->drm.pdev; struct i915_ggtt *ggtt = &dev_priv->ggtt; struct fb_info *info; struct drm_framebuffer *fb; struct i915_vma *vma; unsigned long flags = 0; bool prealloc = false; void __iomem *vaddr; int ret; if (intel_fb && (sizes->fb_width > intel_fb->base.width || sizes->fb_height > intel_fb->base.height)) { DRM_DEBUG_KMS("BIOS fb too small (%dx%d), we require (%dx%d)," " releasing it\n", intel_fb->base.width, intel_fb->base.height, sizes->fb_width, sizes->fb_height); drm_framebuffer_put(&intel_fb->base); intel_fb = ifbdev->fb = NULL; } if (!intel_fb || WARN_ON(!intel_fb_obj(&intel_fb->base))) { DRM_DEBUG_KMS("no BIOS fb, allocating a new one\n"); ret = intelfb_alloc(helper, sizes); if (ret) return ret; intel_fb = ifbdev->fb; } else { DRM_DEBUG_KMS("re-using BIOS fb\n"); prealloc = true; sizes->fb_width = intel_fb->base.width; sizes->fb_height = intel_fb->base.height; } mutex_lock(&dev->struct_mutex); intel_runtime_pm_get(dev_priv); /* Pin the GGTT vma for our access via info->screen_base. * This also validates that any existing fb inherited from the * BIOS is suitable for own access. */ vma = intel_pin_and_fence_fb_obj(&ifbdev->fb->base, DRM_MODE_ROTATE_0, false, &flags); if (IS_ERR(vma)) { ret = PTR_ERR(vma); goto out_unlock; } fb = &ifbdev->fb->base; intel_fb_obj_flush(intel_fb_obj(fb), ORIGIN_DIRTYFB); info = drm_fb_helper_alloc_fbi(helper); if (IS_ERR(info)) { DRM_ERROR("Failed to allocate fb_info\n"); ret = PTR_ERR(info); goto out_unpin; } info->par = helper; ifbdev->helper.fb = fb; strcpy(info->fix.id, "inteldrmfb"); info->fbops = &intelfb_ops; /* setup aperture base/size for vesafb takeover */ info->apertures->ranges[0].base = dev->mode_config.fb_base; info->apertures->ranges[0].size = ggtt->mappable_end; info->fix.smem_start = dev->mode_config.fb_base + i915_ggtt_offset(vma); info->fix.smem_len = vma->node.size; vaddr = i915_vma_pin_iomap(vma); if (IS_ERR(vaddr)) { DRM_ERROR("Failed to remap framebuffer into virtual memory\n"); ret = PTR_ERR(vaddr); goto out_unpin; } info->screen_base = vaddr; info->screen_size = vma->node.size; /* This driver doesn't need a VT switch to restore the mode on resume */ info->skip_vt_switch = true; drm_fb_helper_fill_fix(info, fb->pitches[0], fb->format->depth); drm_fb_helper_fill_var(info, &ifbdev->helper, sizes->fb_width, sizes->fb_height); /* If the object is shmemfs backed, it will have given us zeroed pages. * If the object is stolen however, it will be full of whatever * garbage was left in there. */ if (intel_fb_obj(fb)->stolen && !prealloc) memset_io(info->screen_base, 0, info->screen_size); /* Use default scratch pixmap (info->pixmap.flags = FB_PIXMAP_SYSTEM) */ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x\n", fb->width, fb->height, i915_ggtt_offset(vma)); ifbdev->vma = vma; ifbdev->vma_flags = flags; intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); vga_switcheroo_client_fb_set(pdev, info); return 0; out_unpin: intel_unpin_fb_vma(vma, flags); out_unlock: intel_runtime_pm_put(dev_priv); mutex_unlock(&dev->struct_mutex); return ret; }
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; }