blob: b3d5120b1f4ff6236643ead0ce2ee7780cd6f875 [file] [log] [blame]
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001/**************************************************************************
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/* Might need a hrtimer here? */
31#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
32
Thomas Hellstrom22ee8612010-05-28 11:22:00 +020033static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb);
34static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +000035
36void vmw_display_unit_cleanup(struct vmw_display_unit *du)
37{
38 if (du->cursor_surface)
39 vmw_surface_unreference(&du->cursor_surface);
40 if (du->cursor_dmabuf)
41 vmw_dmabuf_unreference(&du->cursor_dmabuf);
42 drm_crtc_cleanup(&du->crtc);
43 drm_encoder_cleanup(&du->encoder);
44 drm_connector_cleanup(&du->connector);
45}
46
47/*
48 * Display Unit Cursor functions
49 */
50
51int vmw_cursor_update_image(struct vmw_private *dev_priv,
52 u32 *image, u32 width, u32 height,
53 u32 hotspotX, u32 hotspotY)
54{
55 struct {
56 u32 cmd;
57 SVGAFifoCmdDefineAlphaCursor cursor;
58 } *cmd;
59 u32 image_size = width * height * 4;
60 u32 cmd_size = sizeof(*cmd) + image_size;
61
62 if (!image)
63 return -EINVAL;
64
65 cmd = vmw_fifo_reserve(dev_priv, cmd_size);
66 if (unlikely(cmd == NULL)) {
67 DRM_ERROR("Fifo reserve failed.\n");
68 return -ENOMEM;
69 }
70
71 memset(cmd, 0, sizeof(*cmd));
72
73 memcpy(&cmd[1], image, image_size);
74
75 cmd->cmd = cpu_to_le32(SVGA_CMD_DEFINE_ALPHA_CURSOR);
76 cmd->cursor.id = cpu_to_le32(0);
77 cmd->cursor.width = cpu_to_le32(width);
78 cmd->cursor.height = cpu_to_le32(height);
79 cmd->cursor.hotspotX = cpu_to_le32(hotspotX);
80 cmd->cursor.hotspotY = cpu_to_le32(hotspotY);
81
82 vmw_fifo_commit(dev_priv, cmd_size);
83
84 return 0;
85}
86
87void vmw_cursor_update_position(struct vmw_private *dev_priv,
88 bool show, int x, int y)
89{
90 __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
91 uint32_t count;
92
93 iowrite32(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
94 iowrite32(x, fifo_mem + SVGA_FIFO_CURSOR_X);
95 iowrite32(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
96 count = ioread32(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
97 iowrite32(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
98}
99
100int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
101 uint32_t handle, uint32_t width, uint32_t height)
102{
103 struct vmw_private *dev_priv = vmw_priv(crtc->dev);
104 struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
105 struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
106 struct vmw_surface *surface = NULL;
107 struct vmw_dma_buffer *dmabuf = NULL;
108 int ret;
109
110 if (handle) {
Thomas Hellstrom7a73ba72009-12-22 16:53:41 +0100111 ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
112 handle, &surface);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000113 if (!ret) {
114 if (!surface->snooper.image) {
115 DRM_ERROR("surface not suitable for cursor\n");
116 return -EINVAL;
117 }
118 } else {
119 ret = vmw_user_dmabuf_lookup(tfile,
120 handle, &dmabuf);
121 if (ret) {
122 DRM_ERROR("failed to find surface or dmabuf: %i\n", ret);
123 return -EINVAL;
124 }
125 }
126 }
127
128 /* takedown old cursor */
129 if (du->cursor_surface) {
130 du->cursor_surface->snooper.crtc = NULL;
131 vmw_surface_unreference(&du->cursor_surface);
132 }
133 if (du->cursor_dmabuf)
134 vmw_dmabuf_unreference(&du->cursor_dmabuf);
135
136 /* setup new image */
137 if (surface) {
138 /* vmw_user_surface_lookup takes one reference */
139 du->cursor_surface = surface;
140
141 du->cursor_surface->snooper.crtc = crtc;
142 du->cursor_age = du->cursor_surface->snooper.age;
143 vmw_cursor_update_image(dev_priv, surface->snooper.image,
144 64, 64, du->hotspot_x, du->hotspot_y);
145 } else if (dmabuf) {
146 struct ttm_bo_kmap_obj map;
147 unsigned long kmap_offset;
148 unsigned long kmap_num;
149 void *virtual;
150 bool dummy;
151
152 /* vmw_user_surface_lookup takes one reference */
153 du->cursor_dmabuf = dmabuf;
154
155 kmap_offset = 0;
156 kmap_num = (64*64*4) >> PAGE_SHIFT;
157
158 ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0);
159 if (unlikely(ret != 0)) {
160 DRM_ERROR("reserve failed\n");
161 return -EINVAL;
162 }
163
164 ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map);
165 if (unlikely(ret != 0))
166 goto err_unreserve;
167
168 virtual = ttm_kmap_obj_virtual(&map, &dummy);
169 vmw_cursor_update_image(dev_priv, virtual, 64, 64,
170 du->hotspot_x, du->hotspot_y);
171
172 ttm_bo_kunmap(&map);
173err_unreserve:
174 ttm_bo_unreserve(&dmabuf->base);
175
176 } else {
177 vmw_cursor_update_position(dev_priv, false, 0, 0);
178 return 0;
179 }
180
181 vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y);
182
183 return 0;
184}
185
186int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
187{
188 struct vmw_private *dev_priv = vmw_priv(crtc->dev);
189 struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
190 bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false;
191
192 du->cursor_x = x + crtc->x;
193 du->cursor_y = y + crtc->y;
194
195 vmw_cursor_update_position(dev_priv, shown,
196 du->cursor_x, du->cursor_y);
197
198 return 0;
199}
200
201void vmw_kms_cursor_snoop(struct vmw_surface *srf,
202 struct ttm_object_file *tfile,
203 struct ttm_buffer_object *bo,
204 SVGA3dCmdHeader *header)
205{
206 struct ttm_bo_kmap_obj map;
207 unsigned long kmap_offset;
208 unsigned long kmap_num;
209 SVGA3dCopyBox *box;
210 unsigned box_count;
211 void *virtual;
212 bool dummy;
213 struct vmw_dma_cmd {
214 SVGA3dCmdHeader header;
215 SVGA3dCmdSurfaceDMA dma;
216 } *cmd;
217 int ret;
218
219 cmd = container_of(header, struct vmw_dma_cmd, header);
220
221 /* No snooper installed */
222 if (!srf->snooper.image)
223 return;
224
225 if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
226 DRM_ERROR("face and mipmap for cursors should never != 0\n");
227 return;
228 }
229
230 if (cmd->header.size < 64) {
231 DRM_ERROR("at least one full copy box must be given\n");
232 return;
233 }
234
235 box = (SVGA3dCopyBox *)&cmd[1];
236 box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
237 sizeof(SVGA3dCopyBox);
238
239 if (cmd->dma.guest.pitch != (64 * 4) ||
240 cmd->dma.guest.ptr.offset % PAGE_SIZE ||
241 box->x != 0 || box->y != 0 || box->z != 0 ||
242 box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
243 box->w != 64 || box->h != 64 || box->d != 1 ||
244 box_count != 1) {
245 /* TODO handle none page aligned offsets */
246 /* TODO handle partial uploads and pitch != 256 */
247 /* TODO handle more then one copy (size != 64) */
Lucas De Marchi25985ed2011-03-30 22:57:33 -0300248 DRM_ERROR("lazy programmer, can't handle weird stuff\n");
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000249 return;
250 }
251
252 kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
253 kmap_num = (64*64*4) >> PAGE_SHIFT;
254
255 ret = ttm_bo_reserve(bo, true, false, false, 0);
256 if (unlikely(ret != 0)) {
257 DRM_ERROR("reserve failed\n");
258 return;
259 }
260
261 ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
262 if (unlikely(ret != 0))
263 goto err_unreserve;
264
265 virtual = ttm_kmap_obj_virtual(&map, &dummy);
266
267 memcpy(srf->snooper.image, virtual, 64*64*4);
268 srf->snooper.age++;
269
270 /* we can't call this function from this function since execbuf has
271 * reserved fifo space.
272 *
273 * if (srf->snooper.crtc)
274 * vmw_ldu_crtc_cursor_update_image(dev_priv,
275 * srf->snooper.image, 64, 64,
276 * du->hotspot_x, du->hotspot_y);
277 */
278
279 ttm_bo_kunmap(&map);
280err_unreserve:
281 ttm_bo_unreserve(bo);
282}
283
284void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
285{
286 struct drm_device *dev = dev_priv->dev;
287 struct vmw_display_unit *du;
288 struct drm_crtc *crtc;
289
290 mutex_lock(&dev->mode_config.mutex);
291
292 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
293 du = vmw_crtc_to_du(crtc);
294 if (!du->cursor_surface ||
295 du->cursor_age == du->cursor_surface->snooper.age)
296 continue;
297
298 du->cursor_age = du->cursor_surface->snooper.age;
299 vmw_cursor_update_image(dev_priv,
300 du->cursor_surface->snooper.image,
301 64, 64, du->hotspot_x, du->hotspot_y);
302 }
303
304 mutex_unlock(&dev->mode_config.mutex);
305}
306
307/*
308 * Generic framebuffer code
309 */
310
311int vmw_framebuffer_create_handle(struct drm_framebuffer *fb,
312 struct drm_file *file_priv,
313 unsigned int *handle)
314{
315 if (handle)
316 handle = 0;
317
318 return 0;
319}
320
321/*
322 * Surface framebuffer code
323 */
324
325#define vmw_framebuffer_to_vfbs(x) \
326 container_of(x, struct vmw_framebuffer_surface, base.base)
327
328struct vmw_framebuffer_surface {
329 struct vmw_framebuffer base;
330 struct vmw_surface *surface;
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200331 struct vmw_dma_buffer *buffer;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000332 struct delayed_work d_work;
333 struct mutex work_lock;
334 bool present_fs;
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200335 struct list_head head;
336 struct drm_master *master;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000337};
338
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200339/**
340 * vmw_kms_idle_workqueues - Flush workqueues on this master
341 *
342 * @vmaster - Pointer identifying the master, for the surfaces of which
343 * we idle the dirty work queues.
344 *
345 * This function should be called with the ttm lock held in exclusive mode
346 * to idle all dirty work queues before the fifo is taken down.
347 *
348 * The work task may actually requeue itself, but after the flush returns we're
349 * sure that there's nothing to present, since the ttm lock is held in
350 * exclusive mode, so the fifo will never get used.
351 */
352
353void vmw_kms_idle_workqueues(struct vmw_master *vmaster)
354{
355 struct vmw_framebuffer_surface *entry;
356
357 mutex_lock(&vmaster->fb_surf_mutex);
358 list_for_each_entry(entry, &vmaster->fb_surf, head) {
359 if (cancel_delayed_work_sync(&entry->d_work))
360 (void) entry->d_work.work.func(&entry->d_work.work);
361
362 (void) cancel_delayed_work_sync(&entry->d_work);
363 }
364 mutex_unlock(&vmaster->fb_surf_mutex);
365}
366
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000367void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
368{
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200369 struct vmw_framebuffer_surface *vfbs =
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000370 vmw_framebuffer_to_vfbs(framebuffer);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200371 struct vmw_master *vmaster = vmw_master(vfbs->master);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000372
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200373
374 mutex_lock(&vmaster->fb_surf_mutex);
375 list_del(&vfbs->head);
376 mutex_unlock(&vmaster->fb_surf_mutex);
377
378 cancel_delayed_work_sync(&vfbs->d_work);
379 drm_master_put(&vfbs->master);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000380 drm_framebuffer_cleanup(framebuffer);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200381 vmw_surface_unreference(&vfbs->surface);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000382
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200383 kfree(vfbs);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000384}
385
386static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
387{
388 struct delayed_work *d_work =
389 container_of(work, struct delayed_work, work);
390 struct vmw_framebuffer_surface *vfbs =
391 container_of(d_work, struct vmw_framebuffer_surface, d_work);
392 struct vmw_surface *surf = vfbs->surface;
393 struct drm_framebuffer *framebuffer = &vfbs->base.base;
394 struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
395
396 struct {
397 SVGA3dCmdHeader header;
398 SVGA3dCmdPresent body;
399 SVGA3dCopyRect cr;
400 } *cmd;
401
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200402 /**
403 * Strictly we should take the ttm_lock in read mode before accessing
404 * the fifo, to make sure the fifo is present and up. However,
405 * instead we flush all workqueues under the ttm lock in exclusive mode
406 * before taking down the fifo.
407 */
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000408 mutex_lock(&vfbs->work_lock);
409 if (!vfbs->present_fs)
410 goto out_unlock;
411
412 cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
413 if (unlikely(cmd == NULL))
414 goto out_resched;
415
416 cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
417 cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr));
418 cmd->body.sid = cpu_to_le32(surf->res.id);
419 cmd->cr.x = cpu_to_le32(0);
420 cmd->cr.y = cpu_to_le32(0);
421 cmd->cr.srcx = cmd->cr.x;
422 cmd->cr.srcy = cmd->cr.y;
423 cmd->cr.w = cpu_to_le32(framebuffer->width);
424 cmd->cr.h = cpu_to_le32(framebuffer->height);
425 vfbs->present_fs = false;
426 vmw_fifo_commit(dev_priv, sizeof(*cmd));
427out_resched:
428 /**
429 * Will not re-add if already pending.
430 */
431 schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
432out_unlock:
433 mutex_unlock(&vfbs->work_lock);
434}
435
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200436static int do_surface_dirty_ldu(struct vmw_private *dev_priv,
437 struct vmw_framebuffer *framebuffer,
438 struct vmw_surface *surf,
439 unsigned flags, unsigned color,
440 struct drm_clip_rect *clips,
441 unsigned num_clips, int inc)
442{
443 SVGA3dCopyRect *cr;
444 int i;
445
446 struct {
447 SVGA3dCmdHeader header;
448 SVGA3dCmdPresent body;
449 SVGA3dCopyRect cr;
450 } *cmd;
451
452 cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) *
453 sizeof(cmd->cr));
454 if (unlikely(cmd == NULL)) {
455 DRM_ERROR("Fifo reserve failed.\n");
456 return -ENOMEM;
457 }
458
459 memset(cmd, 0, sizeof(*cmd));
460
461 cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
462 cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips *
463 sizeof(cmd->cr));
464 cmd->body.sid = cpu_to_le32(surf->res.id);
465
466 for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) {
467 cr->x = cpu_to_le16(clips->x1);
468 cr->y = cpu_to_le16(clips->y1);
469 cr->srcx = cr->x;
470 cr->srcy = cr->y;
471 cr->w = cpu_to_le16(clips->x2 - clips->x1);
472 cr->h = cpu_to_le16(clips->y2 - clips->y1);
473 }
474
475 vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) *
476 sizeof(cmd->cr));
477 return 0;
478}
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000479
480int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
Thomas Hellstrom02b00162010-10-05 12:43:02 +0200481 struct drm_file *file_priv,
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000482 unsigned flags, unsigned color,
483 struct drm_clip_rect *clips,
484 unsigned num_clips)
485{
486 struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200487 struct vmw_master *vmaster = vmw_master(file_priv->master);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000488 struct vmw_framebuffer_surface *vfbs =
489 vmw_framebuffer_to_vfbs(framebuffer);
490 struct vmw_surface *surf = vfbs->surface;
491 struct drm_clip_rect norect;
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200492 int ret, inc = 1;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000493
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200494 if (unlikely(vfbs->master != file_priv->master))
495 return -EINVAL;
496
497 ret = ttm_read_lock(&vmaster->lock, true);
498 if (unlikely(ret != 0))
499 return ret;
500
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000501 if (!num_clips ||
502 !(dev_priv->fifo.capabilities &
503 SVGA_FIFO_CAP_SCREEN_OBJECT)) {
504 int ret;
505
506 mutex_lock(&vfbs->work_lock);
507 vfbs->present_fs = true;
508 ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
509 mutex_unlock(&vfbs->work_lock);
510 if (ret) {
511 /**
512 * No work pending, Force immediate present.
513 */
514 vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
515 }
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200516 ttm_read_unlock(&vmaster->lock);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000517 return 0;
518 }
519
520 if (!num_clips) {
521 num_clips = 1;
522 clips = &norect;
523 norect.x1 = norect.y1 = 0;
524 norect.x2 = framebuffer->width;
525 norect.y2 = framebuffer->height;
526 } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
527 num_clips /= 2;
528 inc = 2; /* skip source rects */
529 }
530
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200531 ret = do_surface_dirty_ldu(dev_priv, &vfbs->base, surf,
532 flags, color,
533 clips, num_clips, inc);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000534
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200535 ttm_read_unlock(&vmaster->lock);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000536 return 0;
537}
538
539static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
540 .destroy = vmw_framebuffer_surface_destroy,
541 .dirty = vmw_framebuffer_surface_dirty,
542 .create_handle = vmw_framebuffer_create_handle,
543};
544
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200545static int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200546 struct drm_file *file_priv,
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200547 struct vmw_surface *surface,
548 struct vmw_framebuffer **out,
549 const struct drm_mode_fb_cmd
550 *mode_cmd)
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000551
552{
553 struct drm_device *dev = dev_priv->dev;
554 struct vmw_framebuffer_surface *vfbs;
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200555 enum SVGA3dSurfaceFormat format;
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200556 struct vmw_master *vmaster = vmw_master(file_priv->master);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000557 int ret;
558
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200559 /*
560 * Sanity checks.
561 */
562
563 if (unlikely(surface->mip_levels[0] != 1 ||
564 surface->num_sizes != 1 ||
565 surface->sizes[0].width < mode_cmd->width ||
566 surface->sizes[0].height < mode_cmd->height ||
567 surface->sizes[0].depth != 1)) {
568 DRM_ERROR("Incompatible surface dimensions "
569 "for requested mode.\n");
570 return -EINVAL;
571 }
572
573 switch (mode_cmd->depth) {
574 case 32:
575 format = SVGA3D_A8R8G8B8;
576 break;
577 case 24:
578 format = SVGA3D_X8R8G8B8;
579 break;
580 case 16:
581 format = SVGA3D_R5G6B5;
582 break;
583 case 15:
584 format = SVGA3D_A1R5G5B5;
585 break;
Michel Dänzerf01b7ba2011-08-31 07:42:47 +0000586 case 8:
587 format = SVGA3D_LUMINANCE8;
588 break;
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200589 default:
590 DRM_ERROR("Invalid color depth: %d\n", mode_cmd->depth);
591 return -EINVAL;
592 }
593
594 if (unlikely(format != surface->format)) {
595 DRM_ERROR("Invalid surface format for requested mode.\n");
596 return -EINVAL;
597 }
598
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000599 vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
600 if (!vfbs) {
601 ret = -ENOMEM;
602 goto out_err1;
603 }
604
605 ret = drm_framebuffer_init(dev, &vfbs->base.base,
606 &vmw_framebuffer_surface_funcs);
607 if (ret)
608 goto out_err2;
609
610 if (!vmw_surface_reference(surface)) {
611 DRM_ERROR("failed to reference surface %p\n", surface);
612 goto out_err3;
613 }
614
615 /* XXX get the first 3 from the surface info */
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200616 vfbs->base.base.bits_per_pixel = mode_cmd->bpp;
617 vfbs->base.base.pitch = mode_cmd->pitch;
618 vfbs->base.base.depth = mode_cmd->depth;
619 vfbs->base.base.width = mode_cmd->width;
620 vfbs->base.base.height = mode_cmd->height;
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200621 vfbs->base.pin = &vmw_surface_dmabuf_pin;
622 vfbs->base.unpin = &vmw_surface_dmabuf_unpin;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000623 vfbs->surface = surface;
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200624 vfbs->master = drm_master_get(file_priv->master);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000625 mutex_init(&vfbs->work_lock);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200626
627 mutex_lock(&vmaster->fb_surf_mutex);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000628 INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200629 list_add_tail(&vfbs->head, &vmaster->fb_surf);
630 mutex_unlock(&vmaster->fb_surf_mutex);
631
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000632 *out = &vfbs->base;
633
634 return 0;
635
636out_err3:
637 drm_framebuffer_cleanup(&vfbs->base.base);
638out_err2:
639 kfree(vfbs);
640out_err1:
641 return ret;
642}
643
644/*
645 * Dmabuf framebuffer code
646 */
647
648#define vmw_framebuffer_to_vfbd(x) \
649 container_of(x, struct vmw_framebuffer_dmabuf, base.base)
650
651struct vmw_framebuffer_dmabuf {
652 struct vmw_framebuffer base;
653 struct vmw_dma_buffer *buffer;
654};
655
656void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
657{
658 struct vmw_framebuffer_dmabuf *vfbd =
659 vmw_framebuffer_to_vfbd(framebuffer);
660
661 drm_framebuffer_cleanup(framebuffer);
662 vmw_dmabuf_unreference(&vfbd->buffer);
663
664 kfree(vfbd);
665}
666
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200667static int do_dmabuf_dirty_ldu(struct vmw_private *dev_priv,
668 struct vmw_framebuffer *framebuffer,
669 struct vmw_dma_buffer *buffer,
670 unsigned flags, unsigned color,
671 struct drm_clip_rect *clips,
672 unsigned num_clips, int increment)
673{
674 size_t fifo_size;
675 int i;
676
677 struct {
678 uint32_t header;
679 SVGAFifoCmdUpdate body;
680 } *cmd;
681
682 fifo_size = sizeof(*cmd) * num_clips;
683 cmd = vmw_fifo_reserve(dev_priv, fifo_size);
684 if (unlikely(cmd == NULL)) {
685 DRM_ERROR("Fifo reserve failed.\n");
686 return -ENOMEM;
687 }
688
689 memset(cmd, 0, fifo_size);
690 for (i = 0; i < num_clips; i++, clips += increment) {
691 cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
692 cmd[i].body.x = cpu_to_le32(clips->x1);
693 cmd[i].body.y = cpu_to_le32(clips->y1);
694 cmd[i].body.width = cpu_to_le32(clips->x2 - clips->x1);
695 cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);
696 }
697
698 vmw_fifo_commit(dev_priv, fifo_size);
699 return 0;
700}
701
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000702int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
Thomas Hellstrom02b00162010-10-05 12:43:02 +0200703 struct drm_file *file_priv,
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000704 unsigned flags, unsigned color,
705 struct drm_clip_rect *clips,
706 unsigned num_clips)
707{
708 struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200709 struct vmw_master *vmaster = vmw_master(file_priv->master);
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200710 struct vmw_framebuffer_dmabuf *vfbd =
711 vmw_framebuffer_to_vfbd(framebuffer);
712 struct vmw_dma_buffer *dmabuf = vfbd->buffer;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000713 struct drm_clip_rect norect;
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200714 int ret, increment = 1;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000715
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200716 ret = ttm_read_lock(&vmaster->lock, true);
717 if (unlikely(ret != 0))
718 return ret;
719
Thomas Hellstromdf1c93b2010-01-13 22:28:36 +0100720 if (!num_clips) {
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000721 num_clips = 1;
722 clips = &norect;
723 norect.x1 = norect.y1 = 0;
724 norect.x2 = framebuffer->width;
725 norect.y2 = framebuffer->height;
726 } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
727 num_clips /= 2;
728 increment = 2;
729 }
730
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200731 ret = do_dmabuf_dirty_ldu(dev_priv, &vfbd->base, dmabuf,
732 flags, color,
733 clips, num_clips, increment);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000734
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200735 ttm_read_unlock(&vmaster->lock);
Jakob Bornecrantz5deb65c2011-10-04 20:13:18 +0200736 return ret;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000737}
738
739static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
740 .destroy = vmw_framebuffer_dmabuf_destroy,
741 .dirty = vmw_framebuffer_dmabuf_dirty,
742 .create_handle = vmw_framebuffer_create_handle,
743};
744
Jakob Bornecrantz497a3ff2011-10-04 20:13:14 +0200745/**
746 * We need to reserve the start of vram because the host might
747 * scribble to it at mode changes, so we need to reserve it.
748 */
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200749static int vmw_surface_dmabuf_pin(struct vmw_framebuffer *vfb)
750{
751 struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
752 struct vmw_framebuffer_surface *vfbs =
753 vmw_framebuffer_to_vfbs(&vfb->base);
754 unsigned long size = vfbs->base.base.pitch * vfbs->base.base.height;
755 int ret;
Jakob Bornecrantz5f898d92011-10-04 20:13:15 +0200756 struct ttm_placement ne_placement = vmw_vram_ne_placement;
757
758 ne_placement.lpfn = (size + (PAGE_SIZE - 1)) / PAGE_SIZE;
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200759
760 vfbs->buffer = kzalloc(sizeof(*vfbs->buffer), GFP_KERNEL);
761 if (unlikely(vfbs->buffer == NULL))
762 return -ENOMEM;
763
764 vmw_overlay_pause_all(dev_priv);
765 ret = vmw_dmabuf_init(dev_priv, vfbs->buffer, size,
766 &vmw_vram_ne_placement,
767 false, &vmw_dmabuf_bo_free);
768 vmw_overlay_resume_all(dev_priv);
Thomas Hellstrom1ef07242010-11-02 13:21:49 +0000769 if (unlikely(ret != 0))
770 vfbs->buffer = NULL;
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200771
772 return ret;
773}
774
Jakob Bornecrantz497a3ff2011-10-04 20:13:14 +0200775/**
776 * See vmw_surface_dmabuf_pin.
777 */
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200778static int vmw_surface_dmabuf_unpin(struct vmw_framebuffer *vfb)
779{
780 struct ttm_buffer_object *bo;
781 struct vmw_framebuffer_surface *vfbs =
782 vmw_framebuffer_to_vfbs(&vfb->base);
783
Thomas Hellstrom1ef07242010-11-02 13:21:49 +0000784 if (unlikely(vfbs->buffer == NULL))
785 return 0;
786
Thomas Hellstrom22ee8612010-05-28 11:22:00 +0200787 bo = &vfbs->buffer->base;
788 ttm_bo_unref(&bo);
789 vfbs->buffer = NULL;
790
791 return 0;
792}
793
Jakob Bornecrantz497a3ff2011-10-04 20:13:14 +0200794/**
795 * Pin the dmabuffer to the start of vram.
796 */
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000797static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
798{
799 struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
800 struct vmw_framebuffer_dmabuf *vfbd =
801 vmw_framebuffer_to_vfbd(&vfb->base);
802 int ret;
803
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +0200804
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000805 vmw_overlay_pause_all(dev_priv);
806
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200807 ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer, true, false);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000808
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000809 vmw_overlay_resume_all(dev_priv);
810
Jakob Bornecrantz316ab132010-05-28 11:22:05 +0200811 WARN_ON(ret != 0);
812
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000813 return 0;
814}
815
816static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
817{
818 struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
819 struct vmw_framebuffer_dmabuf *vfbd =
820 vmw_framebuffer_to_vfbd(&vfb->base);
821
822 if (!vfbd->buffer) {
823 WARN_ON(!vfbd->buffer);
824 return 0;
825 }
826
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200827 return vmw_dmabuf_unpin(dev_priv, vfbd->buffer, false);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000828}
829
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200830static int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
831 struct vmw_dma_buffer *dmabuf,
832 struct vmw_framebuffer **out,
833 const struct drm_mode_fb_cmd
834 *mode_cmd)
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000835
836{
837 struct drm_device *dev = dev_priv->dev;
838 struct vmw_framebuffer_dmabuf *vfbd;
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200839 unsigned int requested_size;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000840 int ret;
841
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200842 requested_size = mode_cmd->height * mode_cmd->pitch;
843 if (unlikely(requested_size > dmabuf->base.num_pages * PAGE_SIZE)) {
844 DRM_ERROR("Screen buffer object size is too small "
845 "for requested mode.\n");
846 return -EINVAL;
847 }
848
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000849 vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
850 if (!vfbd) {
851 ret = -ENOMEM;
852 goto out_err1;
853 }
854
855 ret = drm_framebuffer_init(dev, &vfbd->base.base,
856 &vmw_framebuffer_dmabuf_funcs);
857 if (ret)
858 goto out_err2;
859
860 if (!vmw_dmabuf_reference(dmabuf)) {
861 DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
862 goto out_err3;
863 }
864
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200865 vfbd->base.base.bits_per_pixel = mode_cmd->bpp;
866 vfbd->base.base.pitch = mode_cmd->pitch;
867 vfbd->base.base.depth = mode_cmd->depth;
868 vfbd->base.base.width = mode_cmd->width;
869 vfbd->base.base.height = mode_cmd->height;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000870 vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
871 vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
872 vfbd->buffer = dmabuf;
873 *out = &vfbd->base;
874
875 return 0;
876
877out_err3:
878 drm_framebuffer_cleanup(&vfbd->base.base);
879out_err2:
880 kfree(vfbd);
881out_err1:
882 return ret;
883}
884
885/*
886 * Generic Kernel modesetting functions
887 */
888
889static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
890 struct drm_file *file_priv,
891 struct drm_mode_fb_cmd *mode_cmd)
892{
893 struct vmw_private *dev_priv = vmw_priv(dev);
894 struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
895 struct vmw_framebuffer *vfb = NULL;
896 struct vmw_surface *surface = NULL;
897 struct vmw_dma_buffer *bo = NULL;
Thomas Hellstrome133e732010-10-05 12:43:04 +0200898 u64 required_size;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000899 int ret;
900
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200901 /**
902 * This code should be conditioned on Screen Objects not being used.
903 * If screen objects are used, we can allocate a GMR to hold the
904 * requested framebuffer.
905 */
906
907 required_size = mode_cmd->pitch * mode_cmd->height;
Thomas Hellstrome133e732010-10-05 12:43:04 +0200908 if (unlikely(required_size > (u64) dev_priv->vram_size)) {
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200909 DRM_ERROR("VRAM size is too small for requested mode.\n");
910 return NULL;
911 }
912
913 /**
914 * End conditioned code.
915 */
916
Thomas Hellstrom7a73ba72009-12-22 16:53:41 +0100917 ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
918 mode_cmd->handle, &surface);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000919 if (ret)
920 goto try_dmabuf;
921
Jakob Bornecrantz5ffdb652010-01-30 03:38:08 +0000922 if (!surface->scanout)
923 goto err_not_scanout;
924
Thomas Hellstrom3a939a52010-10-05 12:43:03 +0200925 ret = vmw_kms_new_framebuffer_surface(dev_priv, file_priv, surface,
926 &vfb, mode_cmd);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000927
928 /* vmw_user_surface_lookup takes one ref so does new_fb */
929 vmw_surface_unreference(&surface);
930
931 if (ret) {
932 DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
Chris Wilsoncce13ff2010-08-08 13:36:38 +0100933 return ERR_PTR(ret);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000934 }
935 return &vfb->base;
936
937try_dmabuf:
938 DRM_INFO("%s: trying buffer\n", __func__);
939
940 ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
941 if (ret) {
942 DRM_ERROR("failed to find buffer: %i\n", ret);
Chris Wilsoncce13ff2010-08-08 13:36:38 +0100943 return ERR_PTR(-ENOENT);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000944 }
945
946 ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
Thomas Hellstromd3216a02010-10-05 12:42:59 +0200947 mode_cmd);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000948
949 /* vmw_user_dmabuf_lookup takes one ref so does new_fb */
950 vmw_dmabuf_unreference(&bo);
951
952 if (ret) {
953 DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
Chris Wilsoncce13ff2010-08-08 13:36:38 +0100954 return ERR_PTR(ret);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000955 }
956
957 return &vfb->base;
Jakob Bornecrantz5ffdb652010-01-30 03:38:08 +0000958
959err_not_scanout:
960 DRM_ERROR("surface not marked as scanout\n");
961 /* vmw_user_surface_lookup takes one ref */
962 vmw_surface_unreference(&surface);
963
Chris Wilsoncce13ff2010-08-08 13:36:38 +0100964 return ERR_PTR(-EINVAL);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000965}
966
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000967static struct drm_mode_config_funcs vmw_kms_funcs = {
968 .fb_create = vmw_kms_fb_create,
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000969};
970
971int vmw_kms_init(struct vmw_private *dev_priv)
972{
973 struct drm_device *dev = dev_priv->dev;
974 int ret;
975
976 drm_mode_config_init(dev);
977 dev->mode_config.funcs = &vmw_kms_funcs;
Jakob Bornecrantz3bef3572010-02-09 19:41:57 +0000978 dev->mode_config.min_width = 1;
979 dev->mode_config.min_height = 1;
Jakob Bornecrantz7e71f8a2010-05-28 11:21:54 +0200980 /* assumed largest fb size */
981 dev->mode_config.max_width = 8192;
982 dev->mode_config.max_height = 8192;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000983
984 ret = vmw_kms_init_legacy_display_system(dev_priv);
985
986 return 0;
987}
988
989int vmw_kms_close(struct vmw_private *dev_priv)
990{
991 /*
992 * Docs says we should take the lock before calling this function
993 * but since it destroys encoders and our destructor calls
994 * drm_encoder_cleanup which takes the lock we deadlock.
995 */
996 drm_mode_config_cleanup(dev_priv->dev);
997 vmw_kms_close_legacy_display_system(dev_priv);
998 return 0;
999}
1000
1001int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
1002 struct drm_file *file_priv)
1003{
1004 struct drm_vmw_cursor_bypass_arg *arg = data;
1005 struct vmw_display_unit *du;
1006 struct drm_mode_object *obj;
1007 struct drm_crtc *crtc;
1008 int ret = 0;
1009
1010
1011 mutex_lock(&dev->mode_config.mutex);
1012 if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
1013
1014 list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
1015 du = vmw_crtc_to_du(crtc);
1016 du->hotspot_x = arg->xhot;
1017 du->hotspot_y = arg->yhot;
1018 }
1019
1020 mutex_unlock(&dev->mode_config.mutex);
1021 return 0;
1022 }
1023
1024 obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
1025 if (!obj) {
1026 ret = -EINVAL;
1027 goto out;
1028 }
1029
1030 crtc = obj_to_crtc(obj);
1031 du = vmw_crtc_to_du(crtc);
1032
1033 du->hotspot_x = arg->xhot;
1034 du->hotspot_y = arg->yhot;
1035
1036out:
1037 mutex_unlock(&dev->mode_config.mutex);
1038
1039 return ret;
1040}
1041
Michel Dänzer0bef23f2011-08-31 07:42:50 +00001042int vmw_kms_write_svga(struct vmw_private *vmw_priv,
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001043 unsigned width, unsigned height, unsigned pitch,
Michel Dänzer6558429b2011-08-31 07:42:49 +00001044 unsigned bpp, unsigned depth)
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001045{
1046 if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
1047 vmw_write(vmw_priv, SVGA_REG_PITCHLOCK, pitch);
1048 else if (vmw_fifo_have_pitchlock(vmw_priv))
1049 iowrite32(pitch, vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
1050 vmw_write(vmw_priv, SVGA_REG_WIDTH, width);
1051 vmw_write(vmw_priv, SVGA_REG_HEIGHT, height);
Michel Dänzer6558429b2011-08-31 07:42:49 +00001052 vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, bpp);
Michel Dänzer0bef23f2011-08-31 07:42:50 +00001053
1054 if (vmw_read(vmw_priv, SVGA_REG_DEPTH) != depth) {
1055 DRM_ERROR("Invalid depth %u for %u bpp, host expects %u\n",
1056 depth, bpp, vmw_read(vmw_priv, SVGA_REG_DEPTH));
1057 return -EINVAL;
1058 }
1059
1060 return 0;
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001061}
1062
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001063int vmw_kms_save_vga(struct vmw_private *vmw_priv)
1064{
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001065 struct vmw_vga_topology_state *save;
1066 uint32_t i;
1067
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001068 vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
1069 vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001070 vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001071 if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
1072 vmw_priv->vga_pitchlock =
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001073 vmw_read(vmw_priv, SVGA_REG_PITCHLOCK);
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001074 else if (vmw_fifo_have_pitchlock(vmw_priv))
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001075 vmw_priv->vga_pitchlock = ioread32(vmw_priv->mmio_virt +
1076 SVGA_FIFO_PITCHLOCK);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001077
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001078 if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
1079 return 0;
1080
1081 vmw_priv->num_displays = vmw_read(vmw_priv,
1082 SVGA_REG_NUM_GUEST_DISPLAYS);
1083
Thomas Hellstrom029e50b2010-10-05 12:43:08 +02001084 if (vmw_priv->num_displays == 0)
1085 vmw_priv->num_displays = 1;
1086
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001087 for (i = 0; i < vmw_priv->num_displays; ++i) {
1088 save = &vmw_priv->vga_save[i];
1089 vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i);
1090 save->primary = vmw_read(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY);
1091 save->pos_x = vmw_read(vmw_priv, SVGA_REG_DISPLAY_POSITION_X);
1092 save->pos_y = vmw_read(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y);
1093 save->width = vmw_read(vmw_priv, SVGA_REG_DISPLAY_WIDTH);
1094 save->height = vmw_read(vmw_priv, SVGA_REG_DISPLAY_HEIGHT);
1095 vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
Thomas Hellstrom30c78bb2010-10-01 10:21:48 +02001096 if (i == 0 && vmw_priv->num_displays == 1 &&
1097 save->width == 0 && save->height == 0) {
1098
1099 /*
1100 * It should be fairly safe to assume that these
1101 * values are uninitialized.
1102 */
1103
1104 save->width = vmw_priv->vga_width - save->pos_x;
1105 save->height = vmw_priv->vga_height - save->pos_y;
1106 }
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001107 }
Thomas Hellstrom30c78bb2010-10-01 10:21:48 +02001108
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001109 return 0;
1110}
1111
1112int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
1113{
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001114 struct vmw_vga_topology_state *save;
1115 uint32_t i;
1116
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001117 vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
1118 vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001119 vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
Jakob Bornecrantzd7e19582010-05-28 11:21:59 +02001120 if (vmw_priv->capabilities & SVGA_CAP_PITCHLOCK)
1121 vmw_write(vmw_priv, SVGA_REG_PITCHLOCK,
1122 vmw_priv->vga_pitchlock);
1123 else if (vmw_fifo_have_pitchlock(vmw_priv))
1124 iowrite32(vmw_priv->vga_pitchlock,
1125 vmw_priv->mmio_virt + SVGA_FIFO_PITCHLOCK);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001126
Thomas Hellstrom7c4f7782010-06-01 11:38:17 +02001127 if (!(vmw_priv->capabilities & SVGA_CAP_DISPLAY_TOPOLOGY))
1128 return 0;
1129
1130 for (i = 0; i < vmw_priv->num_displays; ++i) {
1131 save = &vmw_priv->vga_save[i];
1132 vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, i);
1133 vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, save->primary);
1134 vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, save->pos_x);
1135 vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, save->pos_y);
1136 vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, save->width);
1137 vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, save->height);
1138 vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
1139 }
1140
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +00001141 return 0;
1142}
Jakob Bornecrantzd8bd19d2010-06-01 11:54:20 +02001143
Thomas Hellstrome133e732010-10-05 12:43:04 +02001144bool vmw_kms_validate_mode_vram(struct vmw_private *dev_priv,
1145 uint32_t pitch,
1146 uint32_t height)
1147{
1148 return ((u64) pitch * (u64) height) < (u64) dev_priv->vram_size;
1149}
1150
Thomas Hellstrom7a1c2f62010-10-01 10:21:49 +02001151u32 vmw_get_vblank_counter(struct drm_device *dev, int crtc)
1152{
1153 return 0;
1154}
Jakob Bornecrantz626ab772011-10-04 20:13:20 +02001155
1156
1157/*
1158 * Small shared kms functions.
1159 */
1160
1161int vmw_du_update_layout(struct vmw_private *dev_priv, unsigned num,
1162 struct drm_vmw_rect *rects)
1163{
1164 struct drm_device *dev = dev_priv->dev;
1165 struct vmw_display_unit *du;
1166 struct drm_connector *con;
1167 int i;
1168
1169 mutex_lock(&dev->mode_config.mutex);
1170
1171#if 0
1172 DRM_INFO("%s: new layout ", __func__);
1173 for (i = 0; i < (int)num; i++)
1174 DRM_INFO("(%i, %i %ux%u) ", rects[i].x, rects[i].y,
1175 rects[i].w, rects[i].h);
1176 DRM_INFO("\n");
1177#else
1178 (void)i;
1179#endif
1180
1181 list_for_each_entry(con, &dev->mode_config.connector_list, head) {
1182 du = vmw_connector_to_du(con);
1183 if (num > du->unit) {
1184 du->pref_width = rects[du->unit].w;
1185 du->pref_height = rects[du->unit].h;
1186 du->pref_active = true;
1187 } else {
1188 du->pref_width = 800;
1189 du->pref_height = 600;
1190 du->pref_active = false;
1191 }
1192 con->status = vmw_du_connector_detect(con, true);
1193 }
1194
1195 mutex_unlock(&dev->mode_config.mutex);
1196
1197 return 0;
1198}
1199
1200void vmw_du_crtc_save(struct drm_crtc *crtc)
1201{
1202}
1203
1204void vmw_du_crtc_restore(struct drm_crtc *crtc)
1205{
1206}
1207
1208void vmw_du_crtc_gamma_set(struct drm_crtc *crtc,
1209 u16 *r, u16 *g, u16 *b,
1210 uint32_t start, uint32_t size)
1211{
1212 struct vmw_private *dev_priv = vmw_priv(crtc->dev);
1213 int i;
1214
1215 for (i = 0; i < size; i++) {
1216 DRM_DEBUG("%d r/g/b = 0x%04x / 0x%04x / 0x%04x\n", i,
1217 r[i], g[i], b[i]);
1218 vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 0, r[i] >> 8);
1219 vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 1, g[i] >> 8);
1220 vmw_write(dev_priv, SVGA_PALETTE_BASE + i * 3 + 2, b[i] >> 8);
1221 }
1222}
1223
1224void vmw_du_connector_dpms(struct drm_connector *connector, int mode)
1225{
1226}
1227
1228void vmw_du_connector_save(struct drm_connector *connector)
1229{
1230}
1231
1232void vmw_du_connector_restore(struct drm_connector *connector)
1233{
1234}
1235
1236enum drm_connector_status
1237vmw_du_connector_detect(struct drm_connector *connector, bool force)
1238{
1239 uint32_t num_displays;
1240 struct drm_device *dev = connector->dev;
1241 struct vmw_private *dev_priv = vmw_priv(dev);
1242
1243 mutex_lock(&dev_priv->hw_mutex);
1244 num_displays = vmw_read(dev_priv, SVGA_REG_NUM_DISPLAYS);
1245 mutex_unlock(&dev_priv->hw_mutex);
1246
1247 return ((vmw_connector_to_du(connector)->unit < num_displays) ?
1248 connector_status_connected : connector_status_disconnected);
1249}
1250
1251static struct drm_display_mode vmw_kms_connector_builtin[] = {
1252 /* 640x480@60Hz */
1253 { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
1254 752, 800, 0, 480, 489, 492, 525, 0,
1255 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
1256 /* 800x600@60Hz */
1257 { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 40000, 800, 840,
1258 968, 1056, 0, 600, 601, 605, 628, 0,
1259 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1260 /* 1024x768@60Hz */
1261 { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
1262 1184, 1344, 0, 768, 771, 777, 806, 0,
1263 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
1264 /* 1152x864@75Hz */
1265 { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
1266 1344, 1600, 0, 864, 865, 868, 900, 0,
1267 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1268 /* 1280x768@60Hz */
1269 { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
1270 1472, 1664, 0, 768, 771, 778, 798, 0,
1271 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1272 /* 1280x800@60Hz */
1273 { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
1274 1480, 1680, 0, 800, 803, 809, 831, 0,
1275 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
1276 /* 1280x960@60Hz */
1277 { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
1278 1488, 1800, 0, 960, 961, 964, 1000, 0,
1279 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1280 /* 1280x1024@60Hz */
1281 { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
1282 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
1283 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1284 /* 1360x768@60Hz */
1285 { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
1286 1536, 1792, 0, 768, 771, 777, 795, 0,
1287 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1288 /* 1440x1050@60Hz */
1289 { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
1290 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
1291 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1292 /* 1440x900@60Hz */
1293 { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
1294 1672, 1904, 0, 900, 903, 909, 934, 0,
1295 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1296 /* 1600x1200@60Hz */
1297 { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
1298 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
1299 DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
1300 /* 1680x1050@60Hz */
1301 { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
1302 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
1303 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1304 /* 1792x1344@60Hz */
1305 { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
1306 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
1307 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1308 /* 1853x1392@60Hz */
1309 { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
1310 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
1311 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1312 /* 1920x1200@60Hz */
1313 { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
1314 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
1315 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1316 /* 1920x1440@60Hz */
1317 { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
1318 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
1319 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1320 /* 2560x1600@60Hz */
1321 { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
1322 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
1323 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
1324 /* Terminate */
1325 { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
1326};
1327
1328int vmw_du_connector_fill_modes(struct drm_connector *connector,
1329 uint32_t max_width, uint32_t max_height)
1330{
1331 struct vmw_display_unit *du = vmw_connector_to_du(connector);
1332 struct drm_device *dev = connector->dev;
1333 struct vmw_private *dev_priv = vmw_priv(dev);
1334 struct drm_display_mode *mode = NULL;
1335 struct drm_display_mode *bmode;
1336 struct drm_display_mode prefmode = { DRM_MODE("preferred",
1337 DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
1338 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1339 DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC)
1340 };
1341 int i;
1342
1343 /* Add preferred mode */
1344 {
1345 mode = drm_mode_duplicate(dev, &prefmode);
1346 if (!mode)
1347 return 0;
1348 mode->hdisplay = du->pref_width;
1349 mode->vdisplay = du->pref_height;
1350 mode->vrefresh = drm_mode_vrefresh(mode);
1351 if (vmw_kms_validate_mode_vram(dev_priv, mode->hdisplay * 2,
1352 mode->vdisplay)) {
1353 drm_mode_probed_add(connector, mode);
1354
1355 if (du->pref_mode) {
1356 list_del_init(&du->pref_mode->head);
1357 drm_mode_destroy(dev, du->pref_mode);
1358 }
1359
1360 du->pref_mode = mode;
1361 }
1362 }
1363
1364 for (i = 0; vmw_kms_connector_builtin[i].type != 0; i++) {
1365 bmode = &vmw_kms_connector_builtin[i];
1366 if (bmode->hdisplay > max_width ||
1367 bmode->vdisplay > max_height)
1368 continue;
1369
1370 if (!vmw_kms_validate_mode_vram(dev_priv, bmode->hdisplay * 2,
1371 bmode->vdisplay))
1372 continue;
1373
1374 mode = drm_mode_duplicate(dev, bmode);
1375 if (!mode)
1376 return 0;
1377 mode->vrefresh = drm_mode_vrefresh(mode);
1378
1379 drm_mode_probed_add(connector, mode);
1380 }
1381
1382 drm_mode_connector_list_update(connector);
1383
1384 return 1;
1385}
1386
1387int vmw_du_connector_set_property(struct drm_connector *connector,
1388 struct drm_property *property,
1389 uint64_t val)
1390{
1391 return 0;
1392}