示例#1
0
文件: vmwgfx_scrn.c 项目: Lyude/linux
/**
 * 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);
	}
}
示例#2
0
文件: vmwgfx_stdu.c 项目: mhei/linux
/**
 * 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;
}
示例#3
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);
}
示例#4
0
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;
}
示例#5
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;
}
示例#6
0
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;
}
示例#7
0
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;
}
示例#8
0
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;
}
示例#9
0
文件: vmwgfx_stdu.c 项目: mhei/linux
/**
 * 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;
}
示例#10
0
文件: vmwgfx_stdu.c 项目: mhei/linux
/**
 * 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;
}
示例#11
0
文件: vmwgfx_scrn.c 项目: Lyude/linux
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);
}
示例#12
0
/**
 * 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;
}
示例#13
0
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;
}