Example #1
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;
}
Example #2
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;
}