aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
diff options
context:
space:
mode:
authorJakob Bornecrantz <jakob@vmware.com>2009-12-09 19:19:58 -0500
committerDave Airlie <airlied@redhat.com>2009-12-14 17:38:43 -0500
commitfb1d9738ca053ea8afa5e86af6463155f983b01c (patch)
tree53aa407922c989f48aead5fcf61f9945ca6051d5 /drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
parent632f61178d0473861ba77e774bb654b37bc7eccc (diff)
drm/vmwgfx: Add DRM driver for VMware Virtual GPU
This commit adds the vmwgfx driver for the VWware Virtual GPU aka SVGA. The driver is under staging the same as Nouveau and Radeon KMS. Hopefully the 2D ioctls are bug free and don't need changing, so that part of the API should be stable. But there there is a pretty big chance that the 3D API will change in the future. Signed-off-by: Thomas Hellström <thellstrom@vmware.com> Signed-off-by: Jakob Bornecrantz <jakob@vmware.com> Signed-off-by: Dave Airlie <airlied@redhat.com>
Diffstat (limited to 'drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c')
-rw-r--r--drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c516
1 files changed, 516 insertions, 0 deletions
diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
new file mode 100644
index 000000000000..90891593bf6c
--- /dev/null
+++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
@@ -0,0 +1,516 @@
1/**************************************************************************
2 *
3 * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
4 * All Rights Reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a
7 * copy of this software and associated documentation files (the
8 * "Software"), to deal in the Software without restriction, including
9 * without limitation the rights to use, copy, modify, merge, publish,
10 * distribute, sub license, and/or sell copies of the Software, and to
11 * permit persons to whom the Software is furnished to do so, subject to
12 * the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the
15 * next paragraph) shall be included in all copies or substantial portions
16 * of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 *
26 **************************************************************************/
27
28#include "vmwgfx_kms.h"
29
30#define vmw_crtc_to_ldu(x) \
31 container_of(x, struct vmw_legacy_display_unit, base.crtc)
32#define vmw_encoder_to_ldu(x) \
33 container_of(x, struct vmw_legacy_display_unit, base.encoder)
34#define vmw_connector_to_ldu(x) \
35 container_of(x, struct vmw_legacy_display_unit, base.connector)
36
37struct vmw_legacy_display {
38 struct list_head active;
39
40 unsigned num_active;
41
42 struct vmw_framebuffer *fb;
43};
44
45/**
46 * Display unit using the legacy register interface.
47 */
48struct vmw_legacy_display_unit {
49 struct vmw_display_unit base;
50
51 struct list_head active;
52
53 unsigned unit;
54};
55
56static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
57{
58 list_del_init(&ldu->active);
59 vmw_display_unit_cleanup(&ldu->base);
60 kfree(ldu);
61}
62
63
64/*
65 * Legacy Display Unit CRTC functions
66 */
67
68static void vmw_ldu_crtc_save(struct drm_crtc *crtc)
69{
70}
71
72static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
73{
74}
75
76static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
77 u16 *r, u16 *g, u16 *b,
78 uint32_t size)
79{
80}
81
82static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
83{
84 vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
85}
86
87static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
88{
89 struct vmw_legacy_display *lds = dev_priv->ldu_priv;
90 struct vmw_legacy_display_unit *entry;
91 struct drm_crtc *crtc;
92 int i = 0;
93
94 /* to stop the screen from changing size on resize */
95 vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
96 for (i = 0; i < lds->num_active; i++) {
97 vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
98 vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
99 vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
100 vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
101 vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
102 vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
103 vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
104 }
105
106 /* Now set the mode */
107 vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active);
108 i = 0;
109 list_for_each_entry(entry, &lds->active, active) {
110 crtc = &entry->base.crtc;
111
112 vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
113 vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
114 vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x);
115 vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y);
116 vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay);
117 vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay);
118 vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
119
120 i++;
121 }
122
123 return 0;
124}
125
126static int vmw_ldu_del_active(struct vmw_private *vmw_priv,
127 struct vmw_legacy_display_unit *ldu)
128{
129 struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
130 if (list_empty(&ldu->active))
131 return 0;
132
133 list_del_init(&ldu->active);
134 if (--(ld->num_active) == 0) {
135 BUG_ON(!ld->fb);
136 if (ld->fb->unpin)
137 ld->fb->unpin(ld->fb);
138 ld->fb = NULL;
139 }
140
141 return 0;
142}
143
144static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
145 struct vmw_legacy_display_unit *ldu,
146 struct vmw_framebuffer *vfb)
147{
148 struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
149 struct vmw_legacy_display_unit *entry;
150 struct list_head *at;
151
152 if (!list_empty(&ldu->active))
153 return 0;
154
155 at = &ld->active;
156 list_for_each_entry(entry, &ld->active, active) {
157 if (entry->unit > ldu->unit)
158 break;
159
160 at = &entry->active;
161 }
162
163 list_add(&ldu->active, at);
164 if (ld->num_active++ == 0) {
165 BUG_ON(ld->fb);
166 if (vfb->pin)
167 vfb->pin(vfb);
168 ld->fb = vfb;
169 }
170
171 return 0;
172}
173
174static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
175{
176 struct vmw_private *dev_priv;
177 struct vmw_legacy_display_unit *ldu;
178 struct drm_connector *connector;
179 struct drm_display_mode *mode;
180 struct drm_encoder *encoder;
181 struct vmw_framebuffer *vfb;
182 struct drm_framebuffer *fb;
183 struct drm_crtc *crtc;
184
185 if (!set)
186 return -EINVAL;
187
188 if (!set->crtc)
189 return -EINVAL;
190
191 /* get the ldu */
192 crtc = set->crtc;
193 ldu = vmw_crtc_to_ldu(crtc);
194 vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
195 dev_priv = vmw_priv(crtc->dev);
196
197 if (set->num_connectors > 1) {
198 DRM_ERROR("to many connectors\n");
199 return -EINVAL;
200 }
201
202 if (set->num_connectors == 1 &&
203 set->connectors[0] != &ldu->base.connector) {
204 DRM_ERROR("connector doesn't match %p %p\n",
205 set->connectors[0], &ldu->base.connector);
206 return -EINVAL;
207 }
208
209 /* ldu only supports one fb active at the time */
210 if (dev_priv->ldu_priv->fb && vfb &&
211 dev_priv->ldu_priv->fb != vfb) {
212 DRM_ERROR("Multiple framebuffers not supported\n");
213 return -EINVAL;
214 }
215
216 /* since they always map one to one these are safe */
217 connector = &ldu->base.connector;
218 encoder = &ldu->base.encoder;
219
220 /* should we turn the crtc off? */
221 if (set->num_connectors == 0 || !set->mode || !set->fb) {
222
223 connector->encoder = NULL;
224 encoder->crtc = NULL;
225 crtc->fb = NULL;
226
227 vmw_ldu_del_active(dev_priv, ldu);
228
229 vmw_ldu_commit_list(dev_priv);
230
231 return 0;
232 }
233
234
235 /* we now know we want to set a mode */
236 mode = set->mode;
237 fb = set->fb;
238
239 if (set->x + mode->hdisplay > fb->width ||
240 set->y + mode->vdisplay > fb->height) {
241 DRM_ERROR("set outside of framebuffer\n");
242 return -EINVAL;
243 }
244
245 vmw_fb_off(dev_priv);
246
247 crtc->fb = fb;
248 encoder->crtc = crtc;
249 connector->encoder = encoder;
250 crtc->x = set->x;
251 crtc->y = set->y;
252 crtc->mode = *mode;
253
254 vmw_ldu_add_active(dev_priv, ldu, vfb);
255
256 vmw_ldu_commit_list(dev_priv);
257
258 return 0;
259}
260
261static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
262 .save = vmw_ldu_crtc_save,
263 .restore = vmw_ldu_crtc_restore,
264 .cursor_set = vmw_du_crtc_cursor_set,
265 .cursor_move = vmw_du_crtc_cursor_move,
266 .gamma_set = vmw_ldu_crtc_gamma_set,
267 .destroy = vmw_ldu_crtc_destroy,
268 .set_config = vmw_ldu_crtc_set_config,
269};
270
271/*
272 * Legacy Display Unit encoder functions
273 */
274
275static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder)
276{
277 vmw_ldu_destroy(vmw_encoder_to_ldu(encoder));
278}
279
280static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
281 .destroy = vmw_ldu_encoder_destroy,
282};
283
284/*
285 * Legacy Display Unit connector functions
286 */
287
288static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode)
289{
290}
291
292static void vmw_ldu_connector_save(struct drm_connector *connector)
293{
294}
295
296static void vmw_ldu_connector_restore(struct drm_connector *connector)
297{
298}
299
300static enum drm_connector_status
301 vmw_ldu_connector_detect(struct drm_connector *connector)
302{
303 /* XXX vmwctrl should control connection status */
304 if (vmw_connector_to_ldu(connector)->base.unit == 0)
305 return connector_status_connected;
306 return connector_status_disconnected;
307}
308
309static struct drm_display_mode vmw_ldu_connector_builtin[] = {
310 /* 640x480@60Hz */
311 { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
312 752, 800, 0, 480, 489, 492, 525, 0,
313 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
314 /* 800x600@60Hz */
315 { DRM_MODE("800x600",
316 DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
317 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628,
318 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
319 /* 1024x768@60Hz */
320 { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
321 1184, 1344, 0, 768, 771, 777, 806, 0,
322 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
323 /* 1152x864@75Hz */
324 { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
325 1344, 1600, 0, 864, 865, 868, 900, 0,
326 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
327 /* 1280x768@60Hz */
328 { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
329 1472, 1664, 0, 768, 771, 778, 798, 0,
330 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
331 /* 1280x800@60Hz */
332 { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
333 1480, 1680, 0, 800, 803, 809, 831, 0,
334 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
335 /* 1280x960@60Hz */
336 { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
337 1488, 1800, 0, 960, 961, 964, 1000, 0,
338 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
339 /* 1280x1024@60Hz */
340 { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
341 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
342 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
343 /* 1360x768@60Hz */
344 { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
345 1536, 1792, 0, 768, 771, 777, 795, 0,
346 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
347 /* 1440x1050@60Hz */
348 { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
349 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
350 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
351 /* 1440x900@60Hz */
352 { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
353 1672, 1904, 0, 900, 903, 909, 934, 0,
354 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
355 /* 1600x1200@60Hz */
356 { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
357 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
358 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
359 /* 1680x1050@60Hz */
360 { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
361 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
362 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
363 /* 1792x1344@60Hz */
364 { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
365 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
366 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
367 /* 1853x1392@60Hz */
368 { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
369 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
370 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
371 /* 1920x1200@60Hz */
372 { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
373 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
374 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
375 /* 1920x1440@60Hz */
376 { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
377 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
378 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
379 /* 2560x1600@60Hz */
380 { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
381 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
382 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
383 /* Terminate */
384 { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
385};
386
387static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
388 uint32_t max_width, uint32_t max_height)
389{
390 struct drm_device *dev = connector->dev;
391 struct drm_display_mode *mode = NULL;
392 int i;
393
394 for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
395 if (vmw_ldu_connector_builtin[i].hdisplay > max_width ||
396 vmw_ldu_connector_builtin[i].vdisplay > max_height)
397 continue;
398
399 mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]);
400 if (!mode)
401 return 0;
402 mode->vrefresh = drm_mode_vrefresh(mode);
403
404 drm_mode_probed_add(connector, mode);
405 }
406
407 drm_mode_connector_list_update(connector);
408
409 return 1;
410}
411
412static int vmw_ldu_connector_set_property(struct drm_connector *connector,
413 struct drm_property *property,
414 uint64_t val)
415{
416 return 0;
417}
418
419static void vmw_ldu_connector_destroy(struct drm_connector *connector)
420{
421 vmw_ldu_destroy(vmw_connector_to_ldu(connector));
422}
423
424static struct drm_connector_funcs vmw_legacy_connector_funcs = {
425 .dpms = vmw_ldu_connector_dpms,
426 .save = vmw_ldu_connector_save,
427 .restore = vmw_ldu_connector_restore,
428 .detect = vmw_ldu_connector_detect,
429 .fill_modes = vmw_ldu_connector_fill_modes,
430 .set_property = vmw_ldu_connector_set_property,
431 .destroy = vmw_ldu_connector_destroy,
432};
433
434static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
435{
436 struct vmw_legacy_display_unit *ldu;
437 struct drm_device *dev = dev_priv->dev;
438 struct drm_connector *connector;
439 struct drm_encoder *encoder;
440 struct drm_crtc *crtc;
441
442 ldu = kzalloc(sizeof(*ldu), GFP_KERNEL);
443 if (!ldu)
444 return -ENOMEM;
445
446 ldu->unit = unit;
447 crtc = &ldu->base.crtc;
448 encoder = &ldu->base.encoder;
449 connector = &ldu->base.connector;
450
451 drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
452 DRM_MODE_CONNECTOR_LVDS);
453 /* Initial status */
454 if (unit == 0)
455 connector->status = connector_status_connected;
456 else
457 connector->status = connector_status_disconnected;
458
459 drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
460 DRM_MODE_ENCODER_LVDS);
461 drm_mode_connector_attach_encoder(connector, encoder);
462 encoder->possible_crtcs = (1 << unit);
463 encoder->possible_clones = 0;
464
465 INIT_LIST_HEAD(&ldu->active);
466
467 drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
468
469 drm_connector_attach_property(connector,
470 dev->mode_config.dirty_info_property,
471 1);
472
473 return 0;
474}
475
476int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
477{
478 if (dev_priv->ldu_priv) {
479 DRM_INFO("ldu system already on\n");
480 return -EINVAL;
481 }
482
483 dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv));
484
485 if (!dev_priv->ldu_priv)
486 return -ENOMEM;
487
488 INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
489 dev_priv->ldu_priv->num_active = 0;
490 dev_priv->ldu_priv->fb = NULL;
491
492 drm_mode_create_dirty_info_property(dev_priv->dev);
493
494 vmw_ldu_init(dev_priv, 0);
495 vmw_ldu_init(dev_priv, 1);
496 vmw_ldu_init(dev_priv, 2);
497 vmw_ldu_init(dev_priv, 3);
498 vmw_ldu_init(dev_priv, 4);
499 vmw_ldu_init(dev_priv, 5);
500 vmw_ldu_init(dev_priv, 6);
501 vmw_ldu_init(dev_priv, 7);
502
503 return 0;
504}
505
506int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
507{
508 if (!dev_priv->ldu_priv)
509 return -ENOSYS;
510
511 BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
512
513 kfree(dev_priv->ldu_priv);
514
515 return 0;
516}