blob: 7a7abcdf1020267a3a5b410f7102c8fda36180b7 [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
29#include "drmP.h"
30#include "vmwgfx_drv.h"
31
32#include "ttm/ttm_placement.h"
33
34#include "svga_overlay.h"
35#include "svga_escape.h"
36
37#define VMW_MAX_NUM_STREAMS 1
38
39struct vmw_stream {
40 struct vmw_dma_buffer *buf;
41 bool claimed;
42 bool paused;
43 struct drm_vmw_control_stream_arg saved;
44};
45
46/**
47 * Overlay control
48 */
49struct vmw_overlay {
50 /*
51 * Each stream is a single overlay. In Xv these are called ports.
52 */
53 struct mutex mutex;
54 struct vmw_stream stream[VMW_MAX_NUM_STREAMS];
55};
56
57static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev)
58{
59 struct vmw_private *dev_priv = vmw_priv(dev);
60 return dev_priv ? dev_priv->overlay_priv : NULL;
61}
62
63struct vmw_escape_header {
64 uint32_t cmd;
65 SVGAFifoCmdEscape body;
66};
67
68struct vmw_escape_video_flush {
69 struct vmw_escape_header escape;
70 SVGAEscapeVideoFlush flush;
71};
72
73static inline void fill_escape(struct vmw_escape_header *header,
74 uint32_t size)
75{
76 header->cmd = SVGA_CMD_ESCAPE;
77 header->body.nsid = SVGA_ESCAPE_NSID_VMWARE;
78 header->body.size = size;
79}
80
81static inline void fill_flush(struct vmw_escape_video_flush *cmd,
82 uint32_t stream_id)
83{
84 fill_escape(&cmd->escape, sizeof(cmd->flush));
85 cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
86 cmd->flush.streamId = stream_id;
87}
88
89/**
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +000090 * Send put command to hw.
91 *
92 * Returns
93 * -ERESTARTSYS if interrupted by a signal.
94 */
95static int vmw_overlay_send_put(struct vmw_private *dev_priv,
96 struct vmw_dma_buffer *buf,
97 struct drm_vmw_control_stream_arg *arg,
98 bool interruptible)
99{
100 struct {
101 struct vmw_escape_header escape;
102 struct {
103 struct {
104 uint32_t cmdType;
105 uint32_t streamId;
106 } header;
107 struct {
108 uint32_t registerId;
109 uint32_t value;
110 } items[SVGA_VIDEO_PITCH_3 + 1];
111 } body;
112 struct vmw_escape_video_flush flush;
113 } *cmds;
114 uint32_t offset;
115 int i, ret;
116
117 for (;;) {
118 cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
119 if (cmds)
120 break;
121
122 ret = vmw_fallback_wait(dev_priv, false, true, 0,
123 interruptible, 3*HZ);
124 if (interruptible && ret == -ERESTARTSYS)
125 return ret;
126 else
127 BUG_ON(ret != 0);
128 }
129
130 fill_escape(&cmds->escape, sizeof(cmds->body));
131 cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
132 cmds->body.header.streamId = arg->stream_id;
133
134 for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++)
135 cmds->body.items[i].registerId = i;
136
137 offset = buf->base.offset + arg->offset;
138
139 cmds->body.items[SVGA_VIDEO_ENABLED].value = true;
140 cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags;
141 cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset;
142 cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format;
143 cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
144 cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size;
145 cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width;
146 cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height;
147 cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x;
148 cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
149 cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
150 cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
151 cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x;
152 cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
153 cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
154 cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
155 cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
156 cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
157 cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
158
159 fill_flush(&cmds->flush, arg->stream_id);
160
161 vmw_fifo_commit(dev_priv, sizeof(*cmds));
162
163 return 0;
164}
165
166/**
167 * Send stop command to hw.
168 *
169 * Returns
170 * -ERESTARTSYS if interrupted by a signal.
171 */
172static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
173 uint32_t stream_id,
174 bool interruptible)
175{
176 struct {
177 struct vmw_escape_header escape;
178 SVGAEscapeVideoSetRegs body;
179 struct vmw_escape_video_flush flush;
180 } *cmds;
181 int ret;
182
183 for (;;) {
184 cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
185 if (cmds)
186 break;
187
188 ret = vmw_fallback_wait(dev_priv, false, true, 0,
189 interruptible, 3*HZ);
190 if (interruptible && ret == -ERESTARTSYS)
191 return ret;
192 else
193 BUG_ON(ret != 0);
194 }
195
196 fill_escape(&cmds->escape, sizeof(cmds->body));
197 cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
198 cmds->body.header.streamId = stream_id;
199 cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED;
200 cmds->body.items[0].value = false;
201 fill_flush(&cmds->flush, stream_id);
202
203 vmw_fifo_commit(dev_priv, sizeof(*cmds));
204
205 return 0;
206}
207
208/**
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200209 * Move a buffer to vram, and pin it if @pin.
210 *
211 * XXX: This function is here to be changed at a later date.
212 */
213static int vmw_overlay_move_buffer(struct vmw_private *dev_priv,
214 struct vmw_dma_buffer *buf,
215 bool pin, bool inter)
216{
217 if (pin)
218 return vmw_dmabuf_to_vram(dev_priv, buf, true, inter);
219 else
220 return vmw_dmabuf_unpin(dev_priv, buf, inter);
221}
222
223/**
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000224 * Stop or pause a stream.
225 *
226 * If the stream is paused the no evict flag is removed from the buffer
227 * but left in vram. This allows for instance mode_set to evict it
228 * should it need to.
229 *
230 * The caller must hold the overlay lock.
231 *
232 * @stream_id which stream to stop/pause.
233 * @pause true to pause, false to stop completely.
234 */
235static int vmw_overlay_stop(struct vmw_private *dev_priv,
236 uint32_t stream_id, bool pause,
237 bool interruptible)
238{
239 struct vmw_overlay *overlay = dev_priv->overlay_priv;
240 struct vmw_stream *stream = &overlay->stream[stream_id];
241 int ret;
242
243 /* no buffer attached the stream is completely stopped */
244 if (!stream->buf)
245 return 0;
246
247 /* If the stream is paused this is already done */
248 if (!stream->paused) {
249 ret = vmw_overlay_send_stop(dev_priv, stream_id,
250 interruptible);
251 if (ret)
252 return ret;
253
254 /* We just remove the NO_EVICT flag so no -ENOMEM */
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200255 ret = vmw_overlay_move_buffer(dev_priv, stream->buf, false,
256 interruptible);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000257 if (interruptible && ret == -ERESTARTSYS)
258 return ret;
259 else
260 BUG_ON(ret != 0);
261 }
262
263 if (!pause) {
264 vmw_dmabuf_unreference(&stream->buf);
265 stream->paused = false;
266 } else {
267 stream->paused = true;
268 }
269
270 return 0;
271}
272
273/**
274 * Update a stream and send any put or stop fifo commands needed.
275 *
276 * The caller must hold the overlay lock.
277 *
278 * Returns
279 * -ENOMEM if buffer doesn't fit in vram.
280 * -ERESTARTSYS if interrupted.
281 */
282static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
283 struct vmw_dma_buffer *buf,
284 struct drm_vmw_control_stream_arg *arg,
285 bool interruptible)
286{
287 struct vmw_overlay *overlay = dev_priv->overlay_priv;
288 struct vmw_stream *stream = &overlay->stream[arg->stream_id];
289 int ret = 0;
290
291 if (!buf)
292 return -EINVAL;
293
294 DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__,
295 stream->buf, buf, stream->paused ? "" : "not ");
296
297 if (stream->buf != buf) {
298 ret = vmw_overlay_stop(dev_priv, arg->stream_id,
299 false, interruptible);
300 if (ret)
301 return ret;
302 } else if (!stream->paused) {
303 /* If the buffers match and not paused then just send
304 * the put command, no need to do anything else.
305 */
306 ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
307 if (ret == 0)
308 stream->saved = *arg;
309 else
310 BUG_ON(!interruptible);
311
312 return ret;
313 }
314
315 /* We don't start the old stream if we are interrupted.
316 * Might return -ENOMEM if it can't fit the buffer in vram.
317 */
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200318 ret = vmw_overlay_move_buffer(dev_priv, buf, true, interruptible);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000319 if (ret)
320 return ret;
321
322 ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
323 if (ret) {
324 /* This one needs to happen no matter what. We only remove
325 * the NO_EVICT flag so this is safe from -ENOMEM.
326 */
Jakob Bornecrantzd991ef02011-10-04 20:13:21 +0200327 BUG_ON(vmw_overlay_move_buffer(dev_priv, buf, false, false)
328 != 0);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000329 return ret;
330 }
331
332 if (stream->buf != buf)
333 stream->buf = vmw_dmabuf_reference(buf);
334 stream->saved = *arg;
Jakob Bornecrantz792778e2010-05-28 11:22:04 +0200335 /* stream is no longer stopped/paused */
336 stream->paused = false;
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000337
338 return 0;
339}
340
341/**
342 * Stop all streams.
343 *
344 * Used by the fb code when starting.
345 *
346 * Takes the overlay lock.
347 */
348int vmw_overlay_stop_all(struct vmw_private *dev_priv)
349{
350 struct vmw_overlay *overlay = dev_priv->overlay_priv;
351 int i, ret;
352
353 if (!overlay)
354 return 0;
355
356 mutex_lock(&overlay->mutex);
357
358 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
359 struct vmw_stream *stream = &overlay->stream[i];
360 if (!stream->buf)
361 continue;
362
363 ret = vmw_overlay_stop(dev_priv, i, false, false);
364 WARN_ON(ret != 0);
365 }
366
367 mutex_unlock(&overlay->mutex);
368
369 return 0;
370}
371
372/**
373 * Try to resume all paused streams.
374 *
375 * Used by the kms code after moving a new scanout buffer to vram.
376 *
377 * Takes the overlay lock.
378 */
379int vmw_overlay_resume_all(struct vmw_private *dev_priv)
380{
381 struct vmw_overlay *overlay = dev_priv->overlay_priv;
382 int i, ret;
383
384 if (!overlay)
385 return 0;
386
387 mutex_lock(&overlay->mutex);
388
389 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
390 struct vmw_stream *stream = &overlay->stream[i];
391 if (!stream->paused)
392 continue;
393
394 ret = vmw_overlay_update_stream(dev_priv, stream->buf,
395 &stream->saved, false);
396 if (ret != 0)
397 DRM_INFO("%s: *warning* failed to resume stream %i\n",
398 __func__, i);
399 }
400
401 mutex_unlock(&overlay->mutex);
402
403 return 0;
404}
405
406/**
407 * Pauses all active streams.
408 *
409 * Used by the kms code when moving a new scanout buffer to vram.
410 *
411 * Takes the overlay lock.
412 */
413int vmw_overlay_pause_all(struct vmw_private *dev_priv)
414{
415 struct vmw_overlay *overlay = dev_priv->overlay_priv;
416 int i, ret;
417
418 if (!overlay)
419 return 0;
420
421 mutex_lock(&overlay->mutex);
422
423 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
424 if (overlay->stream[i].paused)
425 DRM_INFO("%s: *warning* stream %i already paused\n",
426 __func__, i);
427 ret = vmw_overlay_stop(dev_priv, i, true, false);
428 WARN_ON(ret != 0);
429 }
430
431 mutex_unlock(&overlay->mutex);
432
433 return 0;
434}
435
436int vmw_overlay_ioctl(struct drm_device *dev, void *data,
437 struct drm_file *file_priv)
438{
439 struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
440 struct vmw_private *dev_priv = vmw_priv(dev);
441 struct vmw_overlay *overlay = dev_priv->overlay_priv;
442 struct drm_vmw_control_stream_arg *arg =
443 (struct drm_vmw_control_stream_arg *)data;
444 struct vmw_dma_buffer *buf;
445 struct vmw_resource *res;
446 int ret;
447
448 if (!overlay)
449 return -ENOSYS;
450
451 ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res);
452 if (ret)
453 return ret;
454
455 mutex_lock(&overlay->mutex);
456
457 if (!arg->enabled) {
458 ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true);
459 goto out_unlock;
460 }
461
462 ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf);
463 if (ret)
464 goto out_unlock;
465
466 ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
467
468 vmw_dmabuf_unreference(&buf);
469
470out_unlock:
471 mutex_unlock(&overlay->mutex);
472 vmw_resource_unreference(&res);
473
474 return ret;
475}
476
477int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
478{
479 if (!dev_priv->overlay_priv)
480 return 0;
481
482 return VMW_MAX_NUM_STREAMS;
483}
484
485int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)
486{
487 struct vmw_overlay *overlay = dev_priv->overlay_priv;
488 int i, k;
489
490 if (!overlay)
491 return 0;
492
493 mutex_lock(&overlay->mutex);
494
495 for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++)
496 if (!overlay->stream[i].claimed)
497 k++;
498
499 mutex_unlock(&overlay->mutex);
500
501 return k;
502}
503
504int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out)
505{
506 struct vmw_overlay *overlay = dev_priv->overlay_priv;
507 int i;
508
509 if (!overlay)
510 return -ENOSYS;
511
512 mutex_lock(&overlay->mutex);
513
514 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
515
516 if (overlay->stream[i].claimed)
517 continue;
518
519 overlay->stream[i].claimed = true;
520 *out = i;
521 mutex_unlock(&overlay->mutex);
522 return 0;
523 }
524
525 mutex_unlock(&overlay->mutex);
526 return -ESRCH;
527}
528
529int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id)
530{
531 struct vmw_overlay *overlay = dev_priv->overlay_priv;
532
533 BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS);
534
535 if (!overlay)
536 return -ENOSYS;
537
538 mutex_lock(&overlay->mutex);
539
540 WARN_ON(!overlay->stream[stream_id].claimed);
541 vmw_overlay_stop(dev_priv, stream_id, false, false);
542 overlay->stream[stream_id].claimed = false;
543
544 mutex_unlock(&overlay->mutex);
545 return 0;
546}
547
548int vmw_overlay_init(struct vmw_private *dev_priv)
549{
550 struct vmw_overlay *overlay;
551 int i;
552
553 if (dev_priv->overlay_priv)
554 return -EINVAL;
555
556 if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_VIDEO) &&
557 (dev_priv->fifo.capabilities & SVGA_FIFO_CAP_ESCAPE)) {
558 DRM_INFO("hardware doesn't support overlays\n");
559 return -ENOSYS;
560 }
561
Rakib Mullickf35119d2011-07-25 17:12:56 -0700562 overlay = kzalloc(sizeof(*overlay), GFP_KERNEL);
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000563 if (!overlay)
564 return -ENOMEM;
565
Jakob Bornecrantzfb1d9732009-12-10 00:19:58 +0000566 mutex_init(&overlay->mutex);
567 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
568 overlay->stream[i].buf = NULL;
569 overlay->stream[i].paused = false;
570 overlay->stream[i].claimed = false;
571 }
572
573 dev_priv->overlay_priv = overlay;
574
575 return 0;
576}
577
578int vmw_overlay_close(struct vmw_private *dev_priv)
579{
580 struct vmw_overlay *overlay = dev_priv->overlay_priv;
581 bool forgotten_buffer = false;
582 int i;
583
584 if (!overlay)
585 return -ENOSYS;
586
587 for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
588 if (overlay->stream[i].buf) {
589 forgotten_buffer = true;
590 vmw_overlay_stop(dev_priv, i, false, false);
591 }
592 }
593
594 WARN_ON(forgotten_buffer);
595
596 dev_priv->overlay_priv = NULL;
597 kfree(overlay);
598
599 return 0;
600}