1/**************************************************************************
2 *
3 * Copyright 2010 Thomas Balling Sørensen & Orasanu Lucian.
4 * Copyright 2014 Advanced Micro Devices, Inc.
5 * All Rights Reserved.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the
9 * "Software"), to deal in the Software without restriction, including
10 * without limitation the rights to use, copy, modify, merge, publish,
11 * distribute, sub license, and/or sell copies of the Software, and to
12 * permit persons to whom the Software is furnished to do so, subject to
13 * the following conditions:
14 *
15 * The above copyright notice and this permission notice (including the
16 * next paragraph) shall be included in all copies or substantial portions
17 * of the Software.
18 *
19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
20 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
22 * IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR
23 * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
24 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
25 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 *
27 **************************************************************************/
28
29#include "pipe/p_screen.h"
30
31#include "util/u_memory.h"
32#include "util/u_handle_table.h"
33#include "util/u_surface.h"
34#include "util/u_video.h"
35
36#include "vl/vl_winsys.h"
37#include "vl/vl_video_buffer.h"
38
39#include "va_private.h"
40
41static const VAImageFormat formats[] =
42{
43   {VA_FOURCC('N','V','1','2')},
44   {VA_FOURCC('I','4','2','0')},
45   {VA_FOURCC('Y','V','1','2')},
46   {VA_FOURCC('Y','U','Y','V')},
47   {VA_FOURCC('U','Y','V','Y')},
48   {.fourcc = VA_FOURCC('B','G','R','A'), .byte_order = VA_LSB_FIRST, 32, 32,
49    0x00ff0000, 0x0000ff00, 0x000000ff, 0xff000000},
50   {.fourcc = VA_FOURCC('R','G','B','A'), .byte_order = VA_LSB_FIRST, 32, 32,
51    0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000},
52   {.fourcc = VA_FOURCC('B','G','R','X'), .byte_order = VA_LSB_FIRST, 32, 24,
53    0x00ff0000, 0x0000ff00, 0x000000ff, 0x00000000},
54   {.fourcc = VA_FOURCC('R','G','B','X'), .byte_order = VA_LSB_FIRST, 32, 24,
55    0x000000ff, 0x0000ff00, 0x00ff0000, 0x00000000}
56};
57
58static void
59vlVaVideoSurfaceSize(vlVaSurface *p_surf, int component,
60                     unsigned *width, unsigned *height)
61{
62   *width = p_surf->templat.width;
63   *height = p_surf->templat.height;
64
65   vl_video_buffer_adjust_size(width, height, component,
66                               p_surf->templat.chroma_format,
67                               p_surf->templat.interlaced);
68}
69
70VAStatus
71vlVaQueryImageFormats(VADriverContextP ctx, VAImageFormat *format_list, int *num_formats)
72{
73   struct pipe_screen *pscreen;
74   enum pipe_format format;
75   int i;
76
77   STATIC_ASSERT(ARRAY_SIZE(formats) == VL_VA_MAX_IMAGE_FORMATS);
78
79   if (!ctx)
80      return VA_STATUS_ERROR_INVALID_CONTEXT;
81
82   if (!(format_list && num_formats))
83      return VA_STATUS_ERROR_INVALID_PARAMETER;
84
85   *num_formats = 0;
86   pscreen = VL_VA_PSCREEN(ctx);
87   for (i = 0; i < ARRAY_SIZE(formats); ++i) {
88      format = VaFourccToPipeFormat(formats[i].fourcc);
89      if (pscreen->is_video_format_supported(pscreen, format,
90          PIPE_VIDEO_PROFILE_UNKNOWN,
91          PIPE_VIDEO_ENTRYPOINT_BITSTREAM))
92         format_list[(*num_formats)++] = formats[i];
93   }
94
95   return VA_STATUS_SUCCESS;
96}
97
98VAStatus
99vlVaCreateImage(VADriverContextP ctx, VAImageFormat *format, int width, int height, VAImage *image)
100{
101   VAStatus status;
102   vlVaDriver *drv;
103   VAImage *img;
104   int w, h;
105
106   if (!ctx)
107      return VA_STATUS_ERROR_INVALID_CONTEXT;
108
109   if (!(format && image && width && height))
110      return VA_STATUS_ERROR_INVALID_PARAMETER;
111
112   drv = VL_VA_DRIVER(ctx);
113
114   img = CALLOC(1, sizeof(VAImage));
115   if (!img)
116      return VA_STATUS_ERROR_ALLOCATION_FAILED;
117   pipe_mutex_lock(drv->mutex);
118   img->image_id = handle_table_add(drv->htab, img);
119   pipe_mutex_unlock(drv->mutex);
120
121   img->format = *format;
122   img->width = width;
123   img->height = height;
124   w = align(width, 2);
125   h = align(height, 2);
126
127   switch (format->fourcc) {
128   case VA_FOURCC('N','V','1','2'):
129      img->num_planes = 2;
130      img->pitches[0] = w;
131      img->offsets[0] = 0;
132      img->pitches[1] = w;
133      img->offsets[1] = w * h;
134      img->data_size  = w * h * 3 / 2;
135      break;
136
137   case VA_FOURCC('I','4','2','0'):
138   case VA_FOURCC('Y','V','1','2'):
139      img->num_planes = 3;
140      img->pitches[0] = w;
141      img->offsets[0] = 0;
142      img->pitches[1] = w / 2;
143      img->offsets[1] = w * h;
144      img->pitches[2] = w / 2;
145      img->offsets[2] = w * h * 5 / 4;
146      img->data_size  = w * h * 3 / 2;
147      break;
148
149   case VA_FOURCC('U','Y','V','Y'):
150   case VA_FOURCC('Y','U','Y','V'):
151      img->num_planes = 1;
152      img->pitches[0] = w * 2;
153      img->offsets[0] = 0;
154      img->data_size  = w * h * 2;
155      break;
156
157   case VA_FOURCC('B','G','R','A'):
158   case VA_FOURCC('R','G','B','A'):
159   case VA_FOURCC('B','G','R','X'):
160   case VA_FOURCC('R','G','B','X'):
161      img->num_planes = 1;
162      img->pitches[0] = w * 4;
163      img->offsets[0] = 0;
164      img->data_size  = w * h * 4;
165      break;
166
167   default:
168      return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
169   }
170
171   status =  vlVaCreateBuffer(ctx, 0, VAImageBufferType,
172                           align(img->data_size, 16),
173                           1, NULL, &img->buf);
174   if (status != VA_STATUS_SUCCESS)
175      return status;
176   *image = *img;
177
178   return status;
179}
180
181VAStatus
182vlVaDeriveImage(VADriverContextP ctx, VASurfaceID surface, VAImage *image)
183{
184   vlVaDriver *drv;
185   vlVaSurface *surf;
186   vlVaBuffer *img_buf;
187   VAImage *img;
188   struct pipe_surface **surfaces;
189   int w;
190   int h;
191   int i;
192
193   if (!ctx)
194      return VA_STATUS_ERROR_INVALID_CONTEXT;
195
196   drv = VL_VA_DRIVER(ctx);
197
198   if (!drv)
199      return VA_STATUS_ERROR_INVALID_CONTEXT;
200
201   surf = handle_table_get(drv->htab, surface);
202
203   if (!surf || !surf->buffer || surf->buffer->interlaced)
204      return VA_STATUS_ERROR_INVALID_SURFACE;
205
206   surfaces = surf->buffer->get_surfaces(surf->buffer);
207   if (!surfaces || !surfaces[0]->texture)
208      return VA_STATUS_ERROR_ALLOCATION_FAILED;
209
210   img = CALLOC(1, sizeof(VAImage));
211   if (!img)
212      return VA_STATUS_ERROR_ALLOCATION_FAILED;
213
214   img->format.fourcc = PipeFormatToVaFourcc(surf->buffer->buffer_format);
215   img->buf = VA_INVALID_ID;
216   img->width = surf->buffer->width;
217   img->height = surf->buffer->height;
218   img->num_palette_entries = 0;
219   img->entry_bytes = 0;
220   w = align(surf->buffer->width, 2);
221   h = align(surf->buffer->height, 2);
222
223   for (i = 0; i < ARRAY_SIZE(formats); ++i) {
224      if (img->format.fourcc == formats[i].fourcc) {
225         img->format = formats[i];
226         break;
227      }
228   }
229
230   switch (img->format.fourcc) {
231   case VA_FOURCC('U','Y','V','Y'):
232   case VA_FOURCC('Y','U','Y','V'):
233      img->num_planes = 1;
234      img->pitches[0] = w * 2;
235      img->offsets[0] = 0;
236      img->data_size  = w * h * 2;
237      break;
238
239   case VA_FOURCC('B','G','R','A'):
240   case VA_FOURCC('R','G','B','A'):
241   case VA_FOURCC('B','G','R','X'):
242   case VA_FOURCC('R','G','B','X'):
243      img->num_planes = 1;
244      img->pitches[0] = w * 4;
245      img->offsets[0] = 0;
246      img->data_size  = w * h * 4;
247      break;
248
249   default:
250      /* VaDeriveImage is designed for contiguous planes. */
251      FREE(img);
252      return VA_STATUS_ERROR_INVALID_IMAGE_FORMAT;
253   }
254
255   img_buf = CALLOC(1, sizeof(vlVaBuffer));
256   if (!img_buf) {
257      FREE(img);
258      return VA_STATUS_ERROR_ALLOCATION_FAILED;
259   }
260
261   pipe_mutex_lock(drv->mutex);
262   img->image_id = handle_table_add(drv->htab, img);
263
264   img_buf->type = VAImageBufferType;
265   img_buf->size = img->data_size;
266   img_buf->num_elements = 1;
267
268   pipe_resource_reference(&img_buf->derived_surface.resource, surfaces[0]->texture);
269
270   img->buf = handle_table_add(VL_VA_DRIVER(ctx)->htab, img_buf);
271   pipe_mutex_unlock(drv->mutex);
272
273   *image = *img;
274
275   return VA_STATUS_SUCCESS;
276}
277
278VAStatus
279vlVaDestroyImage(VADriverContextP ctx, VAImageID image)
280{
281   vlVaDriver *drv;
282   VAImage  *vaimage;
283   VAStatus status;
284
285   if (!ctx)
286      return VA_STATUS_ERROR_INVALID_CONTEXT;
287
288   drv = VL_VA_DRIVER(ctx);
289   pipe_mutex_lock(drv->mutex);
290   vaimage = handle_table_get(drv->htab, image);
291   if (!vaimage) {
292      pipe_mutex_unlock(drv->mutex);
293      return VA_STATUS_ERROR_INVALID_IMAGE;
294   }
295
296   handle_table_remove(VL_VA_DRIVER(ctx)->htab, image);
297   pipe_mutex_unlock(drv->mutex);
298   status = vlVaDestroyBuffer(ctx, vaimage->buf);
299   FREE(vaimage);
300   return status;
301}
302
303VAStatus
304vlVaSetImagePalette(VADriverContextP ctx, VAImageID image, unsigned char *palette)
305{
306   if (!ctx)
307      return VA_STATUS_ERROR_INVALID_CONTEXT;
308
309   return VA_STATUS_ERROR_UNIMPLEMENTED;
310}
311
312VAStatus
313vlVaGetImage(VADriverContextP ctx, VASurfaceID surface, int x, int y,
314             unsigned int width, unsigned int height, VAImageID image)
315{
316   vlVaDriver *drv;
317   vlVaSurface *surf;
318   vlVaBuffer *img_buf;
319   VAImage *vaimage;
320   struct pipe_sampler_view **views;
321   enum pipe_format format;
322   bool convert = false;
323   void *data[3];
324   unsigned pitches[3], i, j;
325
326   if (!ctx)
327      return VA_STATUS_ERROR_INVALID_CONTEXT;
328
329   drv = VL_VA_DRIVER(ctx);
330
331   pipe_mutex_lock(drv->mutex);
332   surf = handle_table_get(drv->htab, surface);
333   if (!surf || !surf->buffer) {
334      pipe_mutex_unlock(drv->mutex);
335      return VA_STATUS_ERROR_INVALID_SURFACE;
336   }
337
338   vaimage = handle_table_get(drv->htab, image);
339   if (!vaimage) {
340      pipe_mutex_unlock(drv->mutex);
341      return VA_STATUS_ERROR_INVALID_IMAGE;
342   }
343
344   img_buf = handle_table_get(drv->htab, vaimage->buf);
345   if (!img_buf) {
346      pipe_mutex_unlock(drv->mutex);
347      return VA_STATUS_ERROR_INVALID_BUFFER;
348   }
349
350   format = VaFourccToPipeFormat(vaimage->format.fourcc);
351   if (format == PIPE_FORMAT_NONE) {
352      pipe_mutex_unlock(drv->mutex);
353      return VA_STATUS_ERROR_OPERATION_FAILED;
354   }
355
356   if (format != surf->buffer->buffer_format) {
357      /* support NV12 to YV12 and IYUV conversion now only */
358      if ((format == PIPE_FORMAT_YV12 &&
359          surf->buffer->buffer_format == PIPE_FORMAT_NV12) ||
360          (format == PIPE_FORMAT_IYUV &&
361          surf->buffer->buffer_format == PIPE_FORMAT_NV12))
362         convert = true;
363      else {
364         pipe_mutex_unlock(drv->mutex);
365         return VA_STATUS_ERROR_OPERATION_FAILED;
366      }
367   }
368
369   views = surf->buffer->get_sampler_view_planes(surf->buffer);
370   if (!views) {
371      pipe_mutex_unlock(drv->mutex);
372      return VA_STATUS_ERROR_OPERATION_FAILED;
373   }
374
375   for (i = 0; i < vaimage->num_planes; i++) {
376      data[i] = img_buf->data + vaimage->offsets[i];
377      pitches[i] = vaimage->pitches[i];
378   }
379   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
380      void *tmp_d;
381      unsigned tmp_p;
382      tmp_d  = data[1];
383      data[1] = data[2];
384      data[2] = tmp_d;
385      tmp_p = pitches[1];
386      pitches[1] = pitches[2];
387      pitches[2] = tmp_p;
388   }
389
390   for (i = 0; i < vaimage->num_planes; i++) {
391      unsigned width, height;
392      if (!views[i]) continue;
393      vlVaVideoSurfaceSize(surf, i, &width, &height);
394      for (j = 0; j < views[i]->texture->array_size; ++j) {
395         struct pipe_box box = {0, 0, j, width, height, 1};
396         struct pipe_transfer *transfer;
397         uint8_t *map;
398         map = drv->pipe->transfer_map(drv->pipe, views[i]->texture, 0,
399                  PIPE_TRANSFER_READ, &box, &transfer);
400         if (!map) {
401            pipe_mutex_unlock(drv->mutex);
402            return VA_STATUS_ERROR_OPERATION_FAILED;
403         }
404
405         if (i == 1 && convert) {
406            u_copy_nv12_to_yv12(data, pitches, i, j,
407               transfer->stride, views[i]->texture->array_size,
408               map, box.width, box.height);
409         } else {
410            util_copy_rect(data[i] + pitches[i] * j,
411               views[i]->texture->format,
412               pitches[i] * views[i]->texture->array_size, 0, 0,
413               box.width, box.height, map, transfer->stride, 0, 0);
414         }
415         pipe_transfer_unmap(drv->pipe, transfer);
416      }
417   }
418   pipe_mutex_unlock(drv->mutex);
419
420   return VA_STATUS_SUCCESS;
421}
422
423VAStatus
424vlVaPutImage(VADriverContextP ctx, VASurfaceID surface, VAImageID image,
425             int src_x, int src_y, unsigned int src_width, unsigned int src_height,
426             int dest_x, int dest_y, unsigned int dest_width, unsigned int dest_height)
427{
428   vlVaDriver *drv;
429   vlVaSurface *surf;
430   vlVaBuffer *img_buf;
431   VAImage *vaimage;
432   struct pipe_sampler_view **views;
433   enum pipe_format format;
434   void *data[3];
435   unsigned pitches[3], i, j;
436
437   if (!ctx)
438      return VA_STATUS_ERROR_INVALID_CONTEXT;
439
440   drv = VL_VA_DRIVER(ctx);
441   pipe_mutex_lock(drv->mutex);
442
443   surf = handle_table_get(drv->htab, surface);
444   if (!surf || !surf->buffer) {
445      pipe_mutex_unlock(drv->mutex);
446      return VA_STATUS_ERROR_INVALID_SURFACE;
447   }
448
449   vaimage = handle_table_get(drv->htab, image);
450   if (!vaimage) {
451      pipe_mutex_unlock(drv->mutex);
452      return VA_STATUS_ERROR_INVALID_IMAGE;
453   }
454
455   img_buf = handle_table_get(drv->htab, vaimage->buf);
456   if (!img_buf) {
457      pipe_mutex_unlock(drv->mutex);
458      return VA_STATUS_ERROR_INVALID_BUFFER;
459   }
460
461   if (img_buf->derived_surface.resource) {
462      /* Attempting to transfer derived image to surface */
463      pipe_mutex_unlock(drv->mutex);
464      return VA_STATUS_ERROR_UNIMPLEMENTED;
465   }
466
467   format = VaFourccToPipeFormat(vaimage->format.fourcc);
468
469   if (format == PIPE_FORMAT_NONE) {
470      pipe_mutex_unlock(drv->mutex);
471      return VA_STATUS_ERROR_OPERATION_FAILED;
472   }
473
474   if ((format != surf->buffer->buffer_format) &&
475         ((format != PIPE_FORMAT_YV12) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12)) &&
476         ((format != PIPE_FORMAT_IYUV) || (surf->buffer->buffer_format != PIPE_FORMAT_NV12))) {
477      struct pipe_video_buffer *tmp_buf;
478      struct pipe_video_buffer templat = surf->templat;
479
480      templat.buffer_format = format;
481      tmp_buf = drv->pipe->create_video_buffer(drv->pipe, &templat);
482
483      if (!tmp_buf) {
484         pipe_mutex_unlock(drv->mutex);
485         return VA_STATUS_ERROR_ALLOCATION_FAILED;
486      }
487
488      surf->buffer->destroy(surf->buffer);
489      surf->buffer = tmp_buf;
490      surf->templat.buffer_format = format;
491   }
492
493   views = surf->buffer->get_sampler_view_planes(surf->buffer);
494   if (!views) {
495      pipe_mutex_unlock(drv->mutex);
496      return VA_STATUS_ERROR_OPERATION_FAILED;
497   }
498
499   for (i = 0; i < vaimage->num_planes; i++) {
500      data[i] = img_buf->data + vaimage->offsets[i];
501      pitches[i] = vaimage->pitches[i];
502   }
503   if (vaimage->format.fourcc == VA_FOURCC('I','4','2','0')) {
504      void *tmp_d;
505      unsigned tmp_p;
506      tmp_d  = data[1];
507      data[1] = data[2];
508      data[2] = tmp_d;
509      tmp_p = pitches[1];
510      pitches[1] = pitches[2];
511      pitches[2] = tmp_p;
512   }
513
514   for (i = 0; i < vaimage->num_planes; ++i) {
515      unsigned width, height;
516      if (!views[i]) continue;
517      vlVaVideoSurfaceSize(surf, i, &width, &height);
518      if (((format == PIPE_FORMAT_YV12) || (format == PIPE_FORMAT_IYUV)) &&
519            (surf->buffer->buffer_format == PIPE_FORMAT_NV12)) {
520         struct pipe_transfer *transfer = NULL;
521         uint8_t *map = NULL;
522         struct pipe_box dst_box_1 = {0, 0, 0, width, height, 1};
523         map = drv->pipe->transfer_map(drv->pipe,
524                                       views[i]->texture,
525                                       0,
526                                       PIPE_TRANSFER_DISCARD_RANGE,
527                                       &dst_box_1, &transfer);
528         if (map == NULL)
529            return VA_STATUS_ERROR_OPERATION_FAILED;
530
531         u_copy_yv12_img_to_nv12_surf ((ubyte * const*)data, map, width, height,
532				       pitches[i], transfer->stride, i);
533         pipe_transfer_unmap(drv->pipe, transfer);
534      } else {
535         for (j = 0; j < views[i]->texture->array_size; ++j) {
536            struct pipe_box dst_box = {0, 0, j, width, height, 1};
537            drv->pipe->texture_subdata(drv->pipe, views[i]->texture, 0,
538                                       PIPE_TRANSFER_WRITE, &dst_box,
539                                       data[i] + pitches[i] * j,
540                                       pitches[i] * views[i]->texture->array_size, 0);
541         }
542      }
543   }
544   pipe_mutex_unlock(drv->mutex);
545
546   return VA_STATUS_SUCCESS;
547}
548