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