/** * vmw_sou_crtc_mode_set_nofb - Create new screen * * @crtc: CRTC associated with the new screen * * This function creates/destroys a screen. This function cannot fail, so if * somehow we run into a failure, just do the best we can to get out. */ static void vmw_sou_crtc_mode_set_nofb(struct drm_crtc *crtc) { struct vmw_private *dev_priv; struct vmw_screen_object_unit *sou; struct vmw_framebuffer *vfb; struct drm_framebuffer *fb; struct drm_plane_state *ps; struct vmw_plane_state *vps; int ret; sou = vmw_crtc_to_sou(crtc); dev_priv = vmw_priv(crtc->dev); ps = crtc->primary->state; fb = ps->fb; vps = vmw_plane_state_to_vps(ps); vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; if (sou->defined) { ret = vmw_sou_fifo_destroy(dev_priv, sou); if (ret) { DRM_ERROR("Failed to destroy Screen Object\n"); return; } } if (vfb) { struct drm_connector_state *conn_state; struct vmw_connector_state *vmw_conn_state; int x, y; sou->buffer = vps->bo; sou->buffer_size = vps->bo_size; if (sou->base.is_implicit) { x = crtc->x; y = crtc->y; } else { conn_state = sou->base.connector.state; vmw_conn_state = vmw_connector_state_to_vcs(conn_state); x = vmw_conn_state->gui_x; y = vmw_conn_state->gui_y; } ret = vmw_sou_fifo_create(dev_priv, sou, x, y, &crtc->mode); if (ret) DRM_ERROR("Failed to define Screen Object %dx%d\n", crtc->x, crtc->y); vmw_kms_add_active(dev_priv, &sou->base, vfb); } else { sou->buffer = NULL; sou->buffer_size = 0; vmw_kms_del_active(dev_priv, &sou->base); } }
/** * vmw_stdu_crtc_page_flip - Binds a buffer to a screen target * * @crtc: CRTC to attach FB to * @fb: FB to attach * @event: Event to be posted. This event should've been alloced * using k[mz]alloc, and should've been completely initialized. * @page_flip_flags: Input flags. * * If the STDU uses the same display and content buffers, i.e. a true flip, * this function will replace the existing display buffer with the new content * buffer. * * If the STDU uses different display and content buffers, i.e. a blit, then * only the content buffer will be updated. * * RETURNS: * 0 on success, error code on failure */ static int vmw_stdu_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *new_fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct vmw_screen_target_display_unit *stdu; struct drm_vmw_rect vclips; struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); int ret; dev_priv = vmw_priv(crtc->dev); stdu = vmw_crtc_to_stdu(crtc); if (!stdu->defined || !vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; ret = vmw_stdu_bind_fb(dev_priv, crtc, &crtc->mode, new_fb); if (ret) return ret; if (stdu->base.is_implicit) vmw_kms_update_implicit_fb(dev_priv, crtc); vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; if (vfb->dmabuf) ret = vmw_kms_stdu_dma(dev_priv, NULL, vfb, NULL, NULL, &vclips, 1, 1, true, false); else ret = vmw_kms_stdu_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, NULL); if (ret) return ret; if (event) { struct vmw_fence_obj *fence = NULL; struct drm_file *file_priv = event->base.file_priv; vmw_execbuf_fence_commands(NULL, dev_priv, &fence, NULL); if (!fence) return -ENOMEM; ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, &event->event.tv_sec, &event->event.tv_usec, true); vmw_fence_obj_unreference(&fence); } else { vmw_fifo_flush(dev_priv, false); } return 0; }
/** * Update the implicit fb to the current fb of this crtc. * Must be called with the mode_config mutex held. */ void vmw_kms_screen_object_update_implicit_fb(struct vmw_private *dev_priv, struct drm_crtc *crtc) { struct vmw_screen_object_unit *sou = vmw_crtc_to_sou(crtc); BUG_ON(!sou->base.is_implicit); dev_priv->sou_priv->implicit_fb = vmw_framebuffer_to_vfb(sou->base.crtc.fb); }
static int vmw_sou_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; struct vmw_screen_object_unit *sou; struct drm_connector *connector; struct drm_display_mode *mode; struct drm_encoder *encoder; struct vmw_framebuffer *vfb; struct drm_framebuffer *fb; struct drm_crtc *crtc; int ret = 0; if (!set) return -EINVAL; if (!set->crtc) return -EINVAL; /* get the sou */ crtc = set->crtc; sou = vmw_crtc_to_sou(crtc); vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL; dev_priv = vmw_priv(crtc->dev); if (set->num_connectors > 1) { DRM_ERROR("to many connectors\n"); return -EINVAL; } if (set->num_connectors == 1 && set->connectors[0] != &sou->base.connector) { DRM_ERROR("connector doesn't match %p %p\n", set->connectors[0], &sou->base.connector); return -EINVAL; } /* sou only supports one fb active at the time */ if (sou->base.is_implicit && dev_priv->sou_priv->implicit_fb && vfb && !(dev_priv->sou_priv->num_implicit == 1 && sou->active_implicit) && dev_priv->sou_priv->implicit_fb != vfb) { DRM_ERROR("Multiple framebuffers not supported\n"); return -EINVAL; } /* since they always map one to one these are safe */ connector = &sou->base.connector; encoder = &sou->base.encoder; /* should we turn the crtc off */ if (set->num_connectors == 0 || !set->mode || !set->fb) { ret = vmw_sou_fifo_destroy(dev_priv, sou); /* the hardware has hung don't do anything more */ if (unlikely(ret != 0)) return ret; connector->encoder = NULL; encoder->crtc = NULL; crtc->fb = NULL; crtc->x = 0; crtc->y = 0; crtc->enabled = false; vmw_sou_del_active(dev_priv, sou); vmw_sou_backing_free(dev_priv, sou); return 0; } /* we now know we want to set a mode */ mode = set->mode; fb = set->fb; if (set->x + mode->hdisplay > fb->width || set->y + mode->vdisplay > fb->height) { DRM_ERROR("set outside of framebuffer\n"); return -EINVAL; } vmw_fb_off(dev_priv); if (mode->hdisplay != crtc->mode.hdisplay || mode->vdisplay != crtc->mode.vdisplay) { /* no need to check if depth is different, because backing * store depth is forced to 4 by the device. */ ret = vmw_sou_fifo_destroy(dev_priv, sou); /* the hardware has hung don't do anything more */ if (unlikely(ret != 0)) return ret; vmw_sou_backing_free(dev_priv, sou); } if (!sou->buffer) { /* forced to depth 4 by the device */ size_t size = mode->hdisplay * mode->vdisplay * 4; ret = vmw_sou_backing_alloc(dev_priv, sou, size); if (unlikely(ret != 0)) return ret; } ret = vmw_sou_fifo_create(dev_priv, sou, set->x, set->y, mode); if (unlikely(ret != 0)) { /* * We are in a bit of a situation here, the hardware has * hung and we may or may not have a buffer hanging of * the screen object, best thing to do is not do anything * if we where defined, if not just turn the crtc of. * Not what userspace wants but it needs to htfu. */ if (sou->defined) return ret; connector->encoder = NULL; encoder->crtc = NULL; crtc->fb = NULL; crtc->x = 0; crtc->y = 0; crtc->enabled = false; return ret; } vmw_sou_add_active(dev_priv, sou, vfb); connector->encoder = encoder; encoder->crtc = crtc; crtc->mode = *mode; crtc->fb = fb; crtc->x = set->x; crtc->y = set->y; crtc->enabled = true; return 0; }
int vmw_present_readback_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); struct drm_vmw_present_readback_arg *arg = (struct drm_vmw_present_readback_arg *)data; struct drm_vmw_fence_rep __user *user_fence_rep = (struct drm_vmw_fence_rep __user *) (unsigned long)arg->fence_rep; struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; num_clips = arg->num_clips; clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; if (unlikely(num_clips == 0)) return 0; if (clips_ptr == NULL) { DRM_ERROR("Argument clips_ptr must be specified.\n"); ret = -EINVAL; goto out_clips; } clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); if (clips == NULL) { DRM_ERROR("Failed to allocate clip rect list.\n"); ret = -ENOMEM; goto out_clips; } ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); if (ret) { DRM_ERROR("Failed to copy clip rects from userspace.\n"); ret = -EFAULT; goto out_no_copy; } drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, arg->fb_id); if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -ENOENT; goto out_no_fb; } vfb = vmw_framebuffer_to_vfb(fb); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; goto out_no_ttm_lock; } ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) goto out_no_ttm_lock; ret = vmw_kms_readback(dev_priv, file_priv, vfb, user_fence_rep, clips, num_clips); ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: return ret; }
int vmw_present_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct vmw_private *dev_priv = vmw_priv(dev); struct drm_vmw_present_arg *arg = (struct drm_vmw_present_arg *)data; struct vmw_surface *surface; struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; struct drm_framebuffer *fb; struct vmw_framebuffer *vfb; struct vmw_resource *res; uint32_t num_clips; int ret; num_clips = arg->num_clips; clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; if (unlikely(num_clips == 0)) return 0; if (clips_ptr == NULL) { DRM_ERROR("Variable clips_ptr must be specified.\n"); ret = -EINVAL; goto out_clips; } clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); if (clips == NULL) { DRM_ERROR("Failed to allocate clip rect list.\n"); ret = -ENOMEM; goto out_clips; } ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); if (ret) { DRM_ERROR("Failed to copy clip rects from userspace.\n"); ret = -EFAULT; goto out_no_copy; } drm_modeset_lock_all(dev); fb = drm_framebuffer_lookup(dev, arg->fb_id); if (!fb) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -ENOENT; goto out_no_fb; } vfb = vmw_framebuffer_to_vfb(fb); ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) goto out_no_ttm_lock; ret = vmw_user_resource_lookup_handle(dev_priv, tfile, arg->sid, user_surface_converter, &res); if (ret) goto out_no_surface; surface = vmw_res_to_srf(res); ret = vmw_kms_present(dev_priv, file_priv, vfb, surface, arg->sid, arg->dest_x, arg->dest_y, clips, num_clips); /* vmw_user_surface_lookup takes one ref so does new_fb */ vmw_surface_unreference(&surface); out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: drm_framebuffer_unreference(fb); out_no_fb: drm_modeset_unlock_all(dev); out_no_copy: kfree(clips); out_clips: return ret; }
static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct drm_framebuffer *old_fb = crtc->primary->fb; struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); struct vmw_fence_obj *fence = NULL; struct drm_vmw_rect vclips; int ret; if (!vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; crtc->primary->fb = fb; /* do a full screen dirty update */ vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; if (vfb->dmabuf) ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb, NULL, &vclips, 1, 1, true, &fence); else ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, &fence); if (ret != 0) goto out_no_fence; if (!fence) { ret = -EINVAL; goto out_no_fence; } if (event) { struct drm_file *file_priv = event->base.file_priv; ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, &event->event.tv_sec, &event->event.tv_usec, true); } /* * No need to hold on to this now. The only cleanup * we need to do if we fail is unref the fence. */ vmw_fence_obj_unreference(&fence); if (vmw_crtc_to_du(crtc)->is_implicit) vmw_kms_update_implicit_fb(dev_priv, crtc); return ret; out_no_fence: crtc->primary->fb = old_fb; return ret; }
static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb, struct drm_pending_vblank_event *event, uint32_t flags) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct drm_framebuffer *old_fb = crtc->primary->fb; struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(fb); struct vmw_fence_obj *fence = NULL; struct drm_clip_rect clips; int ret; /* require ScreenObject support for page flipping */ if (!dev_priv->sou_priv) return -ENOSYS; if (!vmw_sou_screen_object_flippable(dev_priv, crtc)) return -EINVAL; crtc->primary->fb = fb; /* do a full screen dirty update */ clips.x1 = clips.y1 = 0; clips.x2 = fb->width; clips.y2 = fb->height; if (vfb->dmabuf) ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb, &clips, 1, 1, true, &fence); else ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, &clips, NULL, NULL, 0, 0, 1, 1, &fence); if (ret != 0) goto out_no_fence; if (!fence) { ret = -EINVAL; goto out_no_fence; } if (event) { struct drm_file *file_priv = event->base.file_priv; ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, &event->event.tv_sec, &event->event.tv_usec, true); } /* * No need to hold on to this now. The only cleanup * we need to do if we fail is unref the fence. */ vmw_fence_obj_unreference(&fence); if (vmw_crtc_to_du(crtc)->is_implicit) vmw_sou_update_implicit_fb(dev_priv, crtc); return ret; out_no_fence: crtc->primary->fb = old_fb; return ret; }
/** * vmw_stdu_crtc_set_config - Sets a mode * * @set: mode parameters * * This function is the device-specific portion of the DRM CRTC mode set. * For the SVGA device, we do this by defining a Screen Target, binding a * GB Surface to that target, and finally update the screen target. * * RETURNS: * 0 on success, error code otherwise */ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; struct vmw_framebuffer *vfb; struct vmw_screen_target_display_unit *stdu; struct drm_display_mode *mode; struct drm_framebuffer *new_fb; struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; bool turning_off; int ret; if (!set || !set->crtc) return -EINVAL; crtc = set->crtc; stdu = vmw_crtc_to_stdu(crtc); mode = set->mode; new_fb = set->fb; dev_priv = vmw_priv(crtc->dev); turning_off = set->num_connectors == 0 || !mode || !new_fb; vfb = (new_fb) ? vmw_framebuffer_to_vfb(new_fb) : NULL; if (set->num_connectors > 1) { DRM_ERROR("Too many connectors\n"); return -EINVAL; } if (set->num_connectors == 1 && set->connectors[0] != &stdu->base.connector) { DRM_ERROR("Connectors don't match %p %p\n", set->connectors[0], &stdu->base.connector); return -EINVAL; } if (!turning_off && (set->x + mode->hdisplay > new_fb->width || set->y + mode->vdisplay > new_fb->height)) { DRM_ERROR("Set outside of framebuffer\n"); return -EINVAL; } /* Only one active implicit frame-buffer at a time. */ mutex_lock(&dev_priv->global_kms_state_mutex); if (!turning_off && stdu->base.is_implicit && dev_priv->implicit_fb && !(dev_priv->num_implicit == 1 && stdu->base.active_implicit) && dev_priv->implicit_fb != vfb) { mutex_unlock(&dev_priv->global_kms_state_mutex); DRM_ERROR("Multiple implicit framebuffers not supported.\n"); return -EINVAL; } mutex_unlock(&dev_priv->global_kms_state_mutex); /* Since they always map one to one these are safe */ connector = &stdu->base.connector; encoder = &stdu->base.encoder; if (stdu->defined) { ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); if (ret) return ret; vmw_stdu_unpin_display(stdu); (void) vmw_stdu_update_st(dev_priv, stdu); vmw_kms_del_active(dev_priv, &stdu->base); ret = vmw_stdu_destroy_st(dev_priv, stdu); if (ret) return ret; crtc->primary->fb = NULL; crtc->enabled = false; encoder->crtc = NULL; connector->encoder = NULL; stdu->content_fb_type = SAME_AS_DISPLAY; crtc->x = set->x; crtc->y = set->y; } if (turning_off) return 0; /* * Steps to displaying a surface, assume surface is already * bound: * 1. define a screen target * 2. bind a fb to the screen target * 3. update that screen target (this is done later by * vmw_kms_stdu_do_surface_dirty_or_present) */ /* * Note on error handling: We can't really restore the crtc to * it's original state on error, but we at least update the * current state to what's submitted to hardware to enable * future recovery. */ vmw_svga_enable(dev_priv); ret = vmw_stdu_define_st(dev_priv, stdu, mode, set->x, set->y); if (ret) return ret; crtc->x = set->x; crtc->y = set->y; crtc->mode = *mode; ret = vmw_stdu_bind_fb(dev_priv, crtc, mode, new_fb); if (ret) return ret; vmw_kms_add_active(dev_priv, &stdu->base, vfb); crtc->enabled = true; connector->encoder = encoder; encoder->crtc = crtc; return 0; }
/** * vmw_stdu_bind_fb - Bind an fb to a defined screen target * * @dev_priv: Pointer to a device private struct. * @crtc: The crtc holding the screen target. * @mode: The mode currently used by the screen target. Must be non-NULL. * @new_fb: The new framebuffer to bind. Must be non-NULL. * * RETURNS: * 0 on success, error code on failure. */ static int vmw_stdu_bind_fb(struct vmw_private *dev_priv, struct drm_crtc *crtc, struct drm_display_mode *mode, struct drm_framebuffer *new_fb) { struct vmw_screen_target_display_unit *stdu = vmw_crtc_to_stdu(crtc); struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); struct vmw_surface *new_display_srf = NULL; enum stdu_content_type new_content_type; struct vmw_framebuffer_surface *new_vfbs; int ret; WARN_ON_ONCE(!stdu->defined); new_vfbs = (vfb->dmabuf) ? NULL : vmw_framebuffer_to_vfbs(new_fb); if (new_vfbs && new_vfbs->surface->base_size.width == mode->hdisplay && new_vfbs->surface->base_size.height == mode->vdisplay) new_content_type = SAME_AS_DISPLAY; else if (vfb->dmabuf) new_content_type = SEPARATE_DMA; else new_content_type = SEPARATE_SURFACE; if (new_content_type != SAME_AS_DISPLAY && !stdu->display_srf) { struct vmw_surface content_srf; struct drm_vmw_size display_base_size = {0}; display_base_size.width = mode->hdisplay; display_base_size.height = mode->vdisplay; display_base_size.depth = 1; /* * If content buffer is a DMA buf, then we have to construct * surface info */ if (new_content_type == SEPARATE_DMA) { switch (new_fb->bits_per_pixel) { case 32: content_srf.format = SVGA3D_X8R8G8B8; break; case 16: content_srf.format = SVGA3D_R5G6B5; break; case 8: content_srf.format = SVGA3D_P8; break; default: DRM_ERROR("Invalid format\n"); return -EINVAL; } content_srf.flags = 0; content_srf.mip_levels[0] = 1; content_srf.multisample_count = 0; } else { content_srf = *new_vfbs->surface; } ret = vmw_surface_gb_priv_define(crtc->dev, 0, /* because kernel visible only */ content_srf.flags, content_srf.format, true, /* a scanout buffer */ content_srf.mip_levels[0], content_srf.multisample_count, 0, display_base_size, &new_display_srf); if (unlikely(ret != 0)) { DRM_ERROR("Could not allocate screen target surface.\n"); return ret; } } else if (new_content_type == SAME_AS_DISPLAY) { new_display_srf = vmw_surface_reference(new_vfbs->surface); } if (new_display_srf) { /* Pin new surface before flipping */ ret = vmw_resource_pin(&new_display_srf->res, false); if (ret) goto out_srf_unref; ret = vmw_stdu_bind_st(dev_priv, stdu, &new_display_srf->res); if (ret) goto out_srf_unpin; /* Unpin and unreference old surface */ vmw_stdu_unpin_display(stdu); /* Transfer the reference */ stdu->display_srf = new_display_srf; new_display_srf = NULL; } crtc->primary->fb = new_fb; stdu->content_fb_type = new_content_type; return 0; out_srf_unpin: vmw_resource_unpin(&new_display_srf->res); out_srf_unref: vmw_surface_unreference(&new_display_srf); return ret; }
static void vmw_sou_primary_plane_atomic_update(struct drm_plane *plane, struct drm_plane_state *old_state) { struct drm_crtc *crtc = plane->state->crtc; struct drm_pending_vblank_event *event = NULL; struct vmw_fence_obj *fence = NULL; int ret; if (crtc && plane->state->fb) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(plane->state->fb); struct drm_vmw_rect vclips; vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; if (vfb->bo) ret = vmw_kms_sou_do_bo_dirty(dev_priv, vfb, NULL, &vclips, 1, 1, true, &fence, crtc); else ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, &fence, crtc); /* * We cannot really fail this function, so if we do, then output * an error and maintain consistent atomic state. */ if (ret != 0) DRM_ERROR("Failed to update screen.\n"); } else { /* * When disabling a plane, CRTC and FB should always be NULL * together, otherwise it's an error. * Here primary plane is being disable so should really blank * the screen object display unit, if not already done. */ return; } event = crtc->state->event; /* * In case of failure and other cases, vblank event will be sent in * vmw_du_crtc_atomic_flush. */ if (event && fence) { struct drm_file *file_priv = event->base.file_priv; ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, &event->event.vbl.tv_sec, &event->event.vbl.tv_usec, true); if (unlikely(ret != 0)) DRM_ERROR("Failed to queue event on fence.\n"); else crtc->state->event = NULL; } if (fence) vmw_fence_obj_unreference(&fence); }
/** * vmw_stdu_crtc_set_config - Sets a mode * * @set: mode parameters * * This function is the device-specific portion of the DRM CRTC mode set. * For the SVGA device, we do this by defining a Screen Target, binding a * GB Surface to that target, and finally update the screen target. * * RETURNS: * 0 on success, error code otherwise */ static int vmw_stdu_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; struct vmw_screen_target_display_unit *stdu; struct vmw_framebuffer *vfb; struct vmw_framebuffer_surface *new_vfbs; struct drm_display_mode *mode; struct drm_framebuffer *new_fb; struct drm_crtc *crtc; struct drm_encoder *encoder; struct drm_connector *connector; int ret; if (!set || !set->crtc) return -EINVAL; crtc = set->crtc; crtc->x = set->x; crtc->y = set->y; stdu = vmw_crtc_to_stdu(crtc); mode = set->mode; new_fb = set->fb; dev_priv = vmw_priv(crtc->dev); if (set->num_connectors > 1) { DRM_ERROR("Too many connectors\n"); return -EINVAL; } if (set->num_connectors == 1 && set->connectors[0] != &stdu->base.connector) { DRM_ERROR("Connectors don't match %p %p\n", set->connectors[0], &stdu->base.connector); return -EINVAL; } /* Since they always map one to one these are safe */ connector = &stdu->base.connector; encoder = &stdu->base.encoder; /* * After this point the CRTC will be considered off unless a new fb * is bound */ if (stdu->defined) { /* Unbind current surface by binding an invalid one */ ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); if (unlikely(ret != 0)) return ret; /* Update Screen Target, display will now be blank */ if (crtc->primary->fb) { vmw_stdu_update_st(dev_priv, stdu); if (unlikely(ret != 0)) return ret; } crtc->primary->fb = NULL; crtc->enabled = false; encoder->crtc = NULL; connector->encoder = NULL; vmw_stdu_unpin_display(stdu); stdu->content_fb = NULL; stdu->content_fb_type = SAME_AS_DISPLAY; ret = vmw_stdu_destroy_st(dev_priv, stdu); /* The hardware is hung, give up */ if (unlikely(ret != 0)) return ret; } /* Any of these conditions means the caller wants CRTC off */ if (set->num_connectors == 0 || !mode || !new_fb) return 0; if (set->x + mode->hdisplay > new_fb->width || set->y + mode->vdisplay > new_fb->height) { DRM_ERROR("Set outside of framebuffer\n"); return -EINVAL; } stdu->content_fb = new_fb; vfb = vmw_framebuffer_to_vfb(stdu->content_fb); if (vfb->dmabuf) stdu->content_fb_type = SEPARATE_DMA; /* * If the requested mode is different than the width and height * of the FB or if the content buffer is a DMA buf, then allocate * a display FB that matches the dimension of the mode */ if (mode->hdisplay != new_fb->width || mode->vdisplay != new_fb->height || stdu->content_fb_type != SAME_AS_DISPLAY) { struct vmw_surface content_srf; struct drm_vmw_size display_base_size = {0}; struct vmw_surface *display_srf; display_base_size.width = mode->hdisplay; display_base_size.height = mode->vdisplay; display_base_size.depth = 1; /* * If content buffer is a DMA buf, then we have to construct * surface info */ if (stdu->content_fb_type == SEPARATE_DMA) { switch (new_fb->bits_per_pixel) { case 32: content_srf.format = SVGA3D_X8R8G8B8; break; case 16: content_srf.format = SVGA3D_R5G6B5; break; case 8: content_srf.format = SVGA3D_P8; break; default: DRM_ERROR("Invalid format\n"); ret = -EINVAL; goto err_unref_content; } content_srf.flags = 0; content_srf.mip_levels[0] = 1; content_srf.multisample_count = 0; } else { stdu->content_fb_type = SEPARATE_SURFACE; new_vfbs = vmw_framebuffer_to_vfbs(new_fb); content_srf = *new_vfbs->surface; } ret = vmw_surface_gb_priv_define(crtc->dev, 0, /* because kernel visible only */ content_srf.flags, content_srf.format, true, /* a scanout buffer */ content_srf.mip_levels[0], content_srf.multisample_count, 0, display_base_size, &display_srf); if (unlikely(ret != 0)) { DRM_ERROR("Cannot allocate a display FB.\n"); goto err_unref_content; } stdu->display_srf = display_srf; } else { new_vfbs = vmw_framebuffer_to_vfbs(new_fb); stdu->display_srf = new_vfbs->surface; } ret = vmw_stdu_pin_display(stdu); if (unlikely(ret != 0)) { stdu->display_srf = NULL; goto err_unref_content; } vmw_svga_enable(dev_priv); /* * Steps to displaying a surface, assume surface is already * bound: * 1. define a screen target * 2. bind a fb to the screen target * 3. update that screen target (this is done later by * vmw_kms_stdu_do_surface_dirty_or_present) */ ret = vmw_stdu_define_st(dev_priv, stdu); if (unlikely(ret != 0)) goto err_unpin_display_and_content; ret = vmw_stdu_bind_st(dev_priv, stdu, &stdu->display_srf->res); if (unlikely(ret != 0)) goto err_unpin_destroy_st; connector->encoder = encoder; encoder->crtc = crtc; crtc->mode = *mode; crtc->primary->fb = new_fb; crtc->enabled = true; return ret; err_unpin_destroy_st: vmw_stdu_destroy_st(dev_priv, stdu); err_unpin_display_and_content: vmw_stdu_unpin_display(stdu); err_unref_content: stdu->content_fb = NULL; return ret; }
static int vmw_sou_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *new_fb, struct drm_pending_vblank_event *event, uint32_t flags, struct drm_modeset_acquire_ctx *ctx) { struct vmw_private *dev_priv = vmw_priv(crtc->dev); struct drm_framebuffer *old_fb = crtc->primary->fb; struct vmw_framebuffer *vfb = vmw_framebuffer_to_vfb(new_fb); struct vmw_fence_obj *fence = NULL; struct drm_vmw_rect vclips; int ret; if (!vmw_kms_crtc_flippable(dev_priv, crtc)) return -EINVAL; flags &= ~DRM_MODE_PAGE_FLIP_ASYNC; ret = drm_atomic_helper_page_flip(crtc, new_fb, NULL, flags, ctx); if (ret) { DRM_ERROR("Page flip error %d.\n", ret); return ret; } /* do a full screen dirty update */ vclips.x = crtc->x; vclips.y = crtc->y; vclips.w = crtc->mode.hdisplay; vclips.h = crtc->mode.vdisplay; if (vfb->dmabuf) ret = vmw_kms_sou_do_dmabuf_dirty(dev_priv, vfb, NULL, &vclips, 1, 1, true, &fence); else ret = vmw_kms_sou_do_surface_dirty(dev_priv, vfb, NULL, &vclips, NULL, 0, 0, 1, 1, &fence); if (ret != 0) goto out_no_fence; if (!fence) { ret = -EINVAL; goto out_no_fence; } if (event) { struct drm_file *file_priv = event->base.file_priv; ret = vmw_event_fence_action_queue(file_priv, fence, &event->base, &event->event.tv_sec, &event->event.tv_usec, true); } /* * No need to hold on to this now. The only cleanup * we need to do if we fail is unref the fence. */ vmw_fence_obj_unreference(&fence); if (vmw_crtc_to_du(crtc)->is_implicit) vmw_kms_update_implicit_fb(dev_priv, crtc); return ret; out_no_fence: drm_atomic_set_fb_for_plane(crtc->primary->state, old_fb); return ret; }
int vmw_present_readback_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct vmw_private *dev_priv = vmw_priv(dev); struct drm_vmw_present_readback_arg *arg = (struct drm_vmw_present_readback_arg *)data; struct drm_vmw_fence_rep __user *user_fence_rep = (struct drm_vmw_fence_rep __user *) (unsigned long)arg->fence_rep; struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; struct drm_mode_object *obj; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; num_clips = arg->num_clips; clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; if (unlikely(num_clips == 0)) return 0; if (clips_ptr == NULL) { DRM_ERROR("Argument clips_ptr must be specified.\n"); ret = -EINVAL; goto out_clips; } clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); if (clips == NULL) { DRM_ERROR("Failed to allocate clip rect list.\n"); ret = -ENOMEM; goto out_clips; } ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); if (ret) { DRM_ERROR("Failed to copy clip rects from userspace.\n"); ret = -EFAULT; goto out_no_copy; } ret = mutex_lock_interruptible(&dev->mode_config.mutex); if (unlikely(ret != 0)) { ret = -ERESTARTSYS; goto out_no_mode_mutex; } obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); if (!vfb->dmabuf) { DRM_ERROR("Framebuffer not dmabuf backed.\n"); ret = -EINVAL; goto out_no_fb; } ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) goto out_no_ttm_lock; ret = vmw_kms_readback(dev_priv, file_priv, vfb, user_fence_rep, clips, num_clips); ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: mutex_unlock(&dev->mode_config.mutex); out_no_mode_mutex: out_no_copy: kfree(clips); out_clips: return ret; }
int vmw_present_ioctl(struct drm_device *dev, void *data, struct drm_file *file_priv) { struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile; struct vmw_private *dev_priv = vmw_priv(dev); struct drm_vmw_present_arg *arg = (struct drm_vmw_present_arg *)data; struct vmw_surface *surface; struct vmw_master *vmaster = vmw_master(file_priv->master); struct drm_vmw_rect __user *clips_ptr; struct drm_vmw_rect *clips = NULL; struct drm_mode_object *obj; struct vmw_framebuffer *vfb; uint32_t num_clips; int ret; num_clips = arg->num_clips; clips_ptr = (struct drm_vmw_rect *)(unsigned long)arg->clips_ptr; if (unlikely(num_clips == 0)) return 0; if (clips_ptr == NULL) { DRM_ERROR("Variable clips_ptr must be specified.\n"); ret = -EINVAL; goto out_clips; } clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); if (clips == NULL) { DRM_ERROR("Failed to allocate clip rect list.\n"); ret = -ENOMEM; goto out_clips; } ret = copy_from_user(clips, clips_ptr, num_clips * sizeof(*clips)); if (ret) { DRM_ERROR("Failed to copy clip rects from userspace.\n"); ret = -EFAULT; goto out_no_copy; } ret = mutex_lock_interruptible(&dev->mode_config.mutex); if (unlikely(ret != 0)) { ret = -ERESTARTSYS; goto out_no_mode_mutex; } obj = drm_mode_object_find(dev, arg->fb_id, DRM_MODE_OBJECT_FB); if (!obj) { DRM_ERROR("Invalid framebuffer id.\n"); ret = -EINVAL; goto out_no_fb; } vfb = vmw_framebuffer_to_vfb(obj_to_fb(obj)); ret = ttm_read_lock(&vmaster->lock, true); if (unlikely(ret != 0)) goto out_no_ttm_lock; ret = vmw_user_surface_lookup_handle(dev_priv, tfile, arg->sid, &surface); if (ret) goto out_no_surface; ret = vmw_kms_present(dev_priv, file_priv, vfb, surface, arg->sid, arg->dest_x, arg->dest_y, clips, num_clips); /* vmw_user_surface_lookup takes one ref so does new_fb */ vmw_surface_unreference(&surface); out_no_surface: ttm_read_unlock(&vmaster->lock); out_no_ttm_lock: out_no_fb: mutex_unlock(&dev->mode_config.mutex); out_no_mode_mutex: out_no_copy: kfree(clips); out_clips: return ret; }