diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c index 0c1241d09d14..40224e39af13 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c @@ -390,6 +390,61 @@ void vmw_du_primary_plane_destroy(struct drm_plane *plane) } +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *new_state) +{ + struct vmw_display_unit *du = vmw_crtc_to_du(new_state->crtc); + int connector_mask = 1 << drm_connector_index(&du->connector); + bool has_primary = new_state->plane_mask & + BIT(drm_plane_index(crtc->primary)); + + /* We always want to have an active plane with an active CRTC */ + if (has_primary != new_state->enable) + return -EINVAL; + + + if (new_state->connector_mask != connector_mask && + new_state->connector_mask != 0) { + DRM_ERROR("Invalid connectors configuration\n"); + return -EINVAL; + } + + /* + * Our virtual device does not have a dot clock, so use the logical + * clock value as the dot clock. + */ + if (new_state->mode.crtc_clock == 0) + new_state->adjusted_mode.crtc_clock = new_state->mode.clock; + + return 0; +} + + +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ +} + + +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state) +{ + struct drm_pending_vblank_event *event = crtc->state->event; + + if (event) { + crtc->state->event = NULL; + + spin_lock_irq(&crtc->dev->event_lock); + if (drm_crtc_vblank_get(crtc) == 0) + drm_crtc_arm_vblank_event(crtc, event); + else + drm_crtc_send_vblank_event(crtc, event); + spin_unlock_irq(&crtc->dev->event_lock); + } + +} + + /** * vmw_du_crtc_duplicate_state - duplicate crtc state * @crtc: DRM crtc diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h index cc50bf35aeca..f711b5da73e5 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h @@ -161,6 +161,7 @@ struct vmw_crtc_state { * @surf Display surface for STDU * @dmabuf display dmabuf for SOU * @content_fb_type Used by STDU. + * @dmabuf_size Size of the dmabuf, used by Screen Object Display Unit * @pinned pin count for STDU display surface */ struct vmw_plane_state { @@ -169,6 +170,7 @@ struct vmw_plane_state { struct vmw_dma_buffer *dmabuf; int content_fb_type; + unsigned long dmabuf_size; int pinned; }; @@ -342,10 +344,17 @@ int vmw_du_cursor_plane_update(struct drm_plane *plane, uint32_t src_x, uint32_t src_y, uint32_t src_w, uint32_t src_h); +/* Atomic Helpers */ void vmw_du_plane_reset(struct drm_plane *plane); struct drm_plane_state *vmw_du_plane_duplicate_state(struct drm_plane *plane); void vmw_du_plane_destroy_state(struct drm_plane *plane, struct drm_plane_state *state); +int vmw_du_crtc_atomic_check(struct drm_crtc *crtc, + struct drm_crtc_state *state); +void vmw_du_crtc_atomic_begin(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); +void vmw_du_crtc_atomic_flush(struct drm_crtc *crtc, + struct drm_crtc_state *old_crtc_state); void vmw_du_crtc_reset(struct drm_crtc *crtc); struct drm_crtc_state *vmw_du_crtc_duplicate_state(struct drm_crtc *crtc); void vmw_du_crtc_destroy_state(struct drm_crtc *crtc, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 276c7440b8b1..d547e8061f6a 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c @@ -167,6 +167,7 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, if (vfb != ld->fb) { if (ld->fb && ld->fb->unpin) ld->fb->unpin(ld->fb); + vmw_svga_enable(vmw_priv); if (vfb->pin) vfb->pin(vfb); ld->fb = vfb; @@ -190,6 +191,68 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, return 0; } +/** + * vmw_ldu_crtc_mode_set_nofb - Enable svga + * + * @crtc: CRTC associated with the new screen + * + * For LDU, just enable the svga + */ +static void vmw_ldu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ +} + +/** + * vmw_ldu_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + * + */ +static void vmw_ldu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +/** + * vmw_ldu_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. Here's + * usually a good place to call vmw_ldu_add_active/vmw_ldu_del_active + * but since for LDU the display plane is closely tied to the + * CRTC, it makes more sense to do those at plane update time. + */ +static void vmw_ldu_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_legacy_display_unit *ldu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + + + ldu = vmw_crtc_to_ldu(crtc); + dev_priv = vmw_priv(crtc->dev); + fb = crtc->primary->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_ldu_add_active(dev_priv, ldu, vfb); + else + vmw_ldu_del_active(dev_priv, ldu); +} + +/** + * vmw_ldu_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_ldu_crtc_helper_disable(struct drm_crtc *crtc) +{ +} + static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; @@ -346,6 +409,20 @@ static const struct drm_plane_funcs vmw_ldu_cursor_funcs = { .atomic_destroy_state = vmw_du_plane_destroy_state, }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_ldu_crtc_helper_funcs = { + .prepare = vmw_ldu_crtc_helper_prepare, + .commit = vmw_ldu_crtc_helper_commit, + .disable = vmw_ldu_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_ldu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) { @@ -445,6 +522,8 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_ldu_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c index 36d42f5656b4..662024c9f351 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_scrn.c @@ -250,6 +250,109 @@ static int vmw_sou_backing_alloc(struct vmw_private *dev_priv, return ret; } +/** + * 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) { + sou->buffer = vps->dmabuf; + sou->buffer_size = vps->dmabuf_size; + + ret = vmw_sou_fifo_create(dev_priv, sou, crtc->x, crtc->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_sou_crtc_helper_prepare - Noop + * + * @crtc: CRTC associated with the new screen + * + * Prepares the CRTC for a mode set, but we don't need to do anything here. + */ +static void vmw_sou_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_helper_commit - Noop + * + * @crtc: CRTC associated with the new screen + * + * This is called after a mode set has been completed. + */ +static void vmw_sou_crtc_helper_commit(struct drm_crtc *crtc) +{ +} + +/** + * vmw_sou_crtc_helper_disable - Turns off CRTC + * + * @crtc: CRTC to be turned off + */ +static void vmw_sou_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_object_unit *sou; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + sou = vmw_crtc_to_sou(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (sou->defined) { + ret = vmw_sou_fifo_destroy(dev_priv, sou); + if (ret) + DRM_ERROR("Failed to destroy Screen Object\n"); + } +} + static int vmw_sou_crtc_set_config(struct drm_mode_set *set) { struct vmw_private *dev_priv; @@ -527,6 +630,20 @@ static const struct drm_plane_funcs vmw_sou_cursor_funcs = { .atomic_destroy_state = vmw_du_plane_destroy_state, }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_sou_crtc_helper_funcs = { + .prepare = vmw_sou_crtc_helper_prepare, + .commit = vmw_sou_crtc_helper_commit, + .disable = vmw_sou_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_sou_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) { @@ -626,6 +743,8 @@ static int vmw_sou_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_sou_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base, diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c index b1fae4975247..6e3cfad336b0 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_stdu.c @@ -500,6 +500,106 @@ out_srf_unref: return ret; } + +/** + * vmw_stdu_crtc_mode_set_nofb - Updates screen target size + * + * @crtc: CRTC associated with the screen target + * + * This function defines/destroys a screen target + * + */ +static void vmw_stdu_crtc_mode_set_nofb(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } + + if (!crtc->state->enable) + return; + + vmw_svga_enable(dev_priv); + ret = vmw_stdu_define_st(dev_priv, stdu, &crtc->mode, crtc->x, crtc->y); + + if (ret) + DRM_ERROR("Failed to define Screen Target of size %dx%d\n", + crtc->x, crtc->y); +} + + +static void vmw_stdu_crtc_helper_prepare(struct drm_crtc *crtc) +{ +} + + +static void vmw_stdu_crtc_helper_commit(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + struct vmw_framebuffer *vfb; + struct drm_framebuffer *fb; + + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + fb = crtc->primary->fb; + + vfb = (fb) ? vmw_framebuffer_to_vfb(fb) : NULL; + + if (vfb) + vmw_kms_add_active(dev_priv, &stdu->base, vfb); + else + vmw_kms_del_active(dev_priv, &stdu->base); +} + +static void vmw_stdu_crtc_helper_disable(struct drm_crtc *crtc) +{ + struct vmw_private *dev_priv; + struct vmw_screen_target_display_unit *stdu; + int ret; + + + if (!crtc) { + DRM_ERROR("CRTC is NULL\n"); + return; + } + + stdu = vmw_crtc_to_stdu(crtc); + dev_priv = vmw_priv(crtc->dev); + + if (stdu->defined) { + ret = vmw_stdu_bind_st(dev_priv, stdu, NULL); + if (ret) + DRM_ERROR("Failed to blank CRTC\n"); + + (void) vmw_stdu_update_st(dev_priv, stdu); + + ret = vmw_stdu_destroy_st(dev_priv, stdu); + if (ret) + DRM_ERROR("Failed to destroy Screen Target\n"); + + stdu->content_fb_type = SAME_AS_DISPLAY; + } +} + /** * vmw_stdu_crtc_set_config - Sets a mode * @@ -1113,6 +1213,21 @@ static const struct drm_plane_funcs vmw_stdu_cursor_funcs = { }; +/* + * Atomic Helpers + */ +static const struct drm_crtc_helper_funcs vmw_stdu_crtc_helper_funcs = { + .prepare = vmw_stdu_crtc_helper_prepare, + .commit = vmw_stdu_crtc_helper_commit, + .disable = vmw_stdu_crtc_helper_disable, + .mode_set = drm_helper_crtc_mode_set, + .mode_set_nofb = vmw_stdu_crtc_mode_set_nofb, + .atomic_check = vmw_du_crtc_atomic_check, + .atomic_begin = vmw_du_crtc_atomic_begin, + .atomic_flush = vmw_du_crtc_atomic_flush, +}; + + /** * vmw_stdu_init - Sets up a Screen Target Display Unit * @@ -1219,6 +1334,8 @@ static int vmw_stdu_init(struct vmw_private *dev_priv, unsigned unit) goto err_free_unregister; } + drm_crtc_helper_add(crtc, &vmw_stdu_crtc_helper_funcs); + drm_mode_crtc_set_gamma_size(crtc, 256); drm_object_attach_property(&connector->base,