/** * 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); } }
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("Too 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; } /* Only one active implicit frame-buffer at a time. */ if (sou->base.is_implicit && dev_priv->implicit_fb && vfb && !(dev_priv->num_implicit == 1 && sou->base.active_implicit) && dev_priv->implicit_fb != vfb) { DRM_ERROR("Multiple implicit 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->primary->fb = NULL; crtc->x = 0; crtc->y = 0; crtc->enabled = false; vmw_kms_del_active(dev_priv, &sou->base); 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_svga_enable(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->primary->fb = NULL; crtc->x = 0; crtc->y = 0; crtc->enabled = false; return ret; } vmw_kms_add_active(dev_priv, &sou->base, vfb); connector->encoder = encoder; encoder->crtc = crtc; crtc->mode = *mode; crtc->primary->fb = fb; crtc->x = set->x; crtc->y = set->y; crtc->enabled = true; return 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_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; }