diff options
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c')
-rw-r--r-- | drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | 106 |
1 files changed, 69 insertions, 37 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c index 90891593bf6..f7094dde18f 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c | |||
@@ -38,6 +38,7 @@ struct vmw_legacy_display { | |||
38 | struct list_head active; | 38 | struct list_head active; |
39 | 39 | ||
40 | unsigned num_active; | 40 | unsigned num_active; |
41 | unsigned last_num_active; | ||
41 | 42 | ||
42 | struct vmw_framebuffer *fb; | 43 | struct vmw_framebuffer *fb; |
43 | }; | 44 | }; |
@@ -49,8 +50,6 @@ struct vmw_legacy_display_unit { | |||
49 | struct vmw_display_unit base; | 50 | struct vmw_display_unit base; |
50 | 51 | ||
51 | struct list_head active; | 52 | struct list_head active; |
52 | |||
53 | unsigned unit; | ||
54 | }; | 53 | }; |
55 | 54 | ||
56 | static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) | 55 | static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu) |
@@ -88,23 +87,44 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) | |||
88 | { | 87 | { |
89 | struct vmw_legacy_display *lds = dev_priv->ldu_priv; | 88 | struct vmw_legacy_display *lds = dev_priv->ldu_priv; |
90 | struct vmw_legacy_display_unit *entry; | 89 | struct vmw_legacy_display_unit *entry; |
91 | struct drm_crtc *crtc; | 90 | struct drm_framebuffer *fb = NULL; |
91 | struct drm_crtc *crtc = NULL; | ||
92 | int i = 0; | 92 | int i = 0; |
93 | 93 | ||
94 | /* to stop the screen from changing size on resize */ | 94 | /* If there is no display topology the host just assumes |
95 | vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0); | 95 | * that the guest will set the same layout as the host. |
96 | for (i = 0; i < lds->num_active; i++) { | 96 | */ |
97 | vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i); | 97 | if (!(dev_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)) { |
98 | vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i); | 98 | int w = 0, h = 0; |
99 | vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0); | 99 | list_for_each_entry(entry, &lds->active, active) { |
100 | vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0); | 100 | crtc = &entry->base.crtc; |
101 | vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0); | 101 | w = max(w, crtc->x + crtc->mode.hdisplay); |
102 | vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0); | 102 | h = max(h, crtc->y + crtc->mode.vdisplay); |
103 | vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID); | 103 | i++; |
104 | } | ||
105 | |||
106 | if (crtc == NULL) | ||
107 | return 0; | ||
108 | fb = entry->base.crtc.fb; | ||
109 | |||
110 | vmw_kms_write_svga(dev_priv, w, h, fb->pitch, | ||
111 | fb->bits_per_pixel, fb->depth); | ||
112 | |||
113 | return 0; | ||
114 | } | ||
115 | |||
116 | if (!list_empty(&lds->active)) { | ||
117 | entry = list_entry(lds->active.next, typeof(*entry), active); | ||
118 | fb = entry->base.crtc.fb; | ||
119 | |||
120 | vmw_kms_write_svga(dev_priv, fb->width, fb->height, fb->pitch, | ||
121 | fb->bits_per_pixel, fb->depth); | ||
104 | } | 122 | } |
105 | 123 | ||
106 | /* Now set the mode */ | 124 | /* Make sure we always show something. */ |
107 | vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active); | 125 | vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, |
126 | lds->num_active ? lds->num_active : 1); | ||
127 | |||
108 | i = 0; | 128 | i = 0; |
109 | list_for_each_entry(entry, &lds->active, active) { | 129 | list_for_each_entry(entry, &lds->active, active) { |
110 | crtc = &entry->base.crtc; | 130 | crtc = &entry->base.crtc; |
@@ -120,6 +140,10 @@ static int vmw_ldu_commit_list(struct vmw_private *dev_priv) | |||
120 | i++; | 140 | i++; |
121 | } | 141 | } |
122 | 142 | ||
143 | BUG_ON(i != lds->num_active); | ||
144 | |||
145 | lds->last_num_active = lds->num_active; | ||
146 | |||
123 | return 0; | 147 | return 0; |
124 | } | 148 | } |
125 | 149 | ||
@@ -130,6 +154,7 @@ static int vmw_ldu_del_active(struct vmw_private *vmw_priv, | |||
130 | if (list_empty(&ldu->active)) | 154 | if (list_empty(&ldu->active)) |
131 | return 0; | 155 | return 0; |
132 | 156 | ||
157 | /* Must init otherwise list_empty(&ldu->active) will not work. */ | ||
133 | list_del_init(&ldu->active); | 158 | list_del_init(&ldu->active); |
134 | if (--(ld->num_active) == 0) { | 159 | if (--(ld->num_active) == 0) { |
135 | BUG_ON(!ld->fb); | 160 | BUG_ON(!ld->fb); |
@@ -149,24 +174,29 @@ static int vmw_ldu_add_active(struct vmw_private *vmw_priv, | |||
149 | struct vmw_legacy_display_unit *entry; | 174 | struct vmw_legacy_display_unit *entry; |
150 | struct list_head *at; | 175 | struct list_head *at; |
151 | 176 | ||
177 | BUG_ON(!ld->num_active && ld->fb); | ||
178 | if (vfb != ld->fb) { | ||
179 | if (ld->fb && ld->fb->unpin) | ||
180 | ld->fb->unpin(ld->fb); | ||
181 | if (vfb->pin) | ||
182 | vfb->pin(vfb); | ||
183 | ld->fb = vfb; | ||
184 | } | ||
185 | |||
152 | if (!list_empty(&ldu->active)) | 186 | if (!list_empty(&ldu->active)) |
153 | return 0; | 187 | return 0; |
154 | 188 | ||
155 | at = &ld->active; | 189 | at = &ld->active; |
156 | list_for_each_entry(entry, &ld->active, active) { | 190 | list_for_each_entry(entry, &ld->active, active) { |
157 | if (entry->unit > ldu->unit) | 191 | if (entry->base.unit > ldu->base.unit) |
158 | break; | 192 | break; |
159 | 193 | ||
160 | at = &entry->active; | 194 | at = &entry->active; |
161 | } | 195 | } |
162 | 196 | ||
163 | list_add(&ldu->active, at); | 197 | list_add(&ldu->active, at); |
164 | if (ld->num_active++ == 0) { | 198 | |
165 | BUG_ON(ld->fb); | 199 | ld->num_active++; |
166 | if (vfb->pin) | ||
167 | vfb->pin(vfb); | ||
168 | ld->fb = vfb; | ||
169 | } | ||
170 | 200 | ||
171 | return 0; | 201 | return 0; |
172 | } | 202 | } |
@@ -208,6 +238,8 @@ static int vmw_ldu_crtc_set_config(struct drm_mode_set *set) | |||
208 | 238 | ||
209 | /* ldu only supports one fb active at the time */ | 239 | /* ldu only supports one fb active at the time */ |
210 | if (dev_priv->ldu_priv->fb && vfb && | 240 | if (dev_priv->ldu_priv->fb && vfb && |
241 | !(dev_priv->ldu_priv->num_active == 1 && | ||
242 | !list_empty(&ldu->active)) && | ||
211 | dev_priv->ldu_priv->fb != vfb) { | 243 | dev_priv->ldu_priv->fb != vfb) { |
212 | DRM_ERROR("Multiple framebuffers not supported\n"); | 244 | DRM_ERROR("Multiple framebuffers not supported\n"); |
213 | return -EINVAL; | 245 | return -EINVAL; |
@@ -443,18 +475,16 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) | |||
443 | if (!ldu) | 475 | if (!ldu) |
444 | return -ENOMEM; | 476 | return -ENOMEM; |
445 | 477 | ||
446 | ldu->unit = unit; | 478 | ldu->base.unit = unit; |
447 | crtc = &ldu->base.crtc; | 479 | crtc = &ldu->base.crtc; |
448 | encoder = &ldu->base.encoder; | 480 | encoder = &ldu->base.encoder; |
449 | connector = &ldu->base.connector; | 481 | connector = &ldu->base.connector; |
450 | 482 | ||
483 | INIT_LIST_HEAD(&ldu->active); | ||
484 | |||
451 | drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, | 485 | drm_connector_init(dev, connector, &vmw_legacy_connector_funcs, |
452 | DRM_MODE_CONNECTOR_LVDS); | 486 | DRM_MODE_CONNECTOR_LVDS); |
453 | /* Initial status */ | 487 | connector->status = vmw_ldu_connector_detect(connector); |
454 | if (unit == 0) | ||
455 | connector->status = connector_status_connected; | ||
456 | else | ||
457 | connector->status = connector_status_disconnected; | ||
458 | 488 | ||
459 | drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, | 489 | drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs, |
460 | DRM_MODE_ENCODER_LVDS); | 490 | DRM_MODE_ENCODER_LVDS); |
@@ -462,8 +492,6 @@ static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit) | |||
462 | encoder->possible_crtcs = (1 << unit); | 492 | encoder->possible_crtcs = (1 << unit); |
463 | encoder->possible_clones = 0; | 493 | encoder->possible_clones = 0; |
464 | 494 | ||
465 | INIT_LIST_HEAD(&ldu->active); | ||
466 | |||
467 | drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); | 495 | drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs); |
468 | 496 | ||
469 | drm_connector_attach_property(connector, | 497 | drm_connector_attach_property(connector, |
@@ -487,18 +515,22 @@ int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv) | |||
487 | 515 | ||
488 | INIT_LIST_HEAD(&dev_priv->ldu_priv->active); | 516 | INIT_LIST_HEAD(&dev_priv->ldu_priv->active); |
489 | dev_priv->ldu_priv->num_active = 0; | 517 | dev_priv->ldu_priv->num_active = 0; |
518 | dev_priv->ldu_priv->last_num_active = 0; | ||
490 | dev_priv->ldu_priv->fb = NULL; | 519 | dev_priv->ldu_priv->fb = NULL; |
491 | 520 | ||
492 | drm_mode_create_dirty_info_property(dev_priv->dev); | 521 | drm_mode_create_dirty_info_property(dev_priv->dev); |
493 | 522 | ||
494 | vmw_ldu_init(dev_priv, 0); | 523 | vmw_ldu_init(dev_priv, 0); |
495 | vmw_ldu_init(dev_priv, 1); | 524 | /* for old hardware without multimon only enable one display */ |
496 | vmw_ldu_init(dev_priv, 2); | 525 | if (dev_priv->capabilities & SVGA_CAP_MULTIMON) { |
497 | vmw_ldu_init(dev_priv, 3); | 526 | vmw_ldu_init(dev_priv, 1); |
498 | vmw_ldu_init(dev_priv, 4); | 527 | vmw_ldu_init(dev_priv, 2); |
499 | vmw_ldu_init(dev_priv, 5); | 528 | vmw_ldu_init(dev_priv, 3); |
500 | vmw_ldu_init(dev_priv, 6); | 529 | vmw_ldu_init(dev_priv, 4); |
501 | vmw_ldu_init(dev_priv, 7); | 530 | vmw_ldu_init(dev_priv, 5); |
531 | vmw_ldu_init(dev_priv, 6); | ||
532 | vmw_ldu_init(dev_priv, 7); | ||
533 | } | ||
502 | 534 | ||
503 | return 0; | 535 | return 0; |
504 | } | 536 | } |