74d8bb59d10e3927e3ec8c82c9c48f3d1f44c14b
[platform/core/uifw/libtdm.git] / src / tdm_helper.c
1 /**************************************************************************
2  *
3  * libtdm
4  *
5  * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
6  *
7  * Contact: Eunchul Kim <chulspro.kim@samsung.com>,
8  *          JinYoung Jeon <jy0.jeon@samsung.com>,
9  *          Taeheon Kim <th908.kim@samsung.com>,
10  *          YoungJun Cho <yj44.cho@samsung.com>,
11  *          SooChan Lim <sc1.lim@samsung.com>,
12  *          Boram Park <sc1.lim@samsung.com>
13  *
14  * Permission is hereby granted, free of charge, to any person obtaining a
15  * copy of this software and associated documentation files (the
16  * "Software"), to deal in the Software without restriction, including
17  * without limitation the rights to use, copy, modify, merge, publish,
18  * distribute, sub license, and/or sell copies of the Software, and to
19  * permit persons to whom the Software is furnished to do so, subject to
20  * the following conditions:
21  *
22  * The above copyright notice and this permission notice (including the
23  * next paragraph) shall be included in all copies or substantial portions
24  * of the Software.
25  *
26  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
27  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT.
29  * IN NO EVENT SHALL PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR
30  * ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
31  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
32  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33  *
34 **************************************************************************/
35
36 #ifdef HAVE_CONFIG_H
37 #include "config.h"
38 #endif
39
40 #include <png.h>
41 #include <string.h>
42 #include <tbm_surface.h>
43 #include <tbm_surface_internal.h>
44 #include <tbm_drm_helper.h>
45 #include <string.h>
46 #include <time.h>
47 #include <pixman.h>
48 #include <inttypes.h>
49
50 #include "tdm.h"
51 #include "tdm_private.h"
52 #include "tdm_helper.h"
53
54 #define PNG_DEPTH 8
55
56 static const char *file_exts[2] = {"png", "yuv"};
57
58 int tdm_dump_enable;
59 char *tdm_debug_dump_dir;
60
61 EXTERN double
62 tdm_helper_get_time(void)
63 {
64         struct timespec tp;
65
66         if (clock_gettime(CLOCK_MONOTONIC, &tp) == 0)
67                 return (double)tp.tv_sec + ((double)tp.tv_nsec) / 1000000000.0;
68
69         return 0;
70 }
71
72 static int
73 _tdm_helper_check_file_is_valid(const char* path, int del_link)
74 {
75         char *real_path;
76
77         if (!path)
78                 return 0;
79
80         real_path = realpath(path, NULL);
81         if (real_path && strncmp(path, real_path, strlen(path))) {
82                 if (del_link)
83                         unlink(path);
84                 free(real_path);
85
86                 return 0;
87         }
88
89         if (real_path)
90                 free(real_path);
91
92         return 1;
93 }
94
95 static void
96 _tdm_helper_dump_raw(const char *file, void *data1, int size1, void *data2,
97                                          int size2, void *data3, int size3)
98 {
99         unsigned int *blocks;
100         FILE *fp;
101
102         if (!_tdm_helper_check_file_is_valid(file, 1))
103                 TDM_WRN("'%s' may be symbolic link\n", file);
104
105         fp = fopen(file, "w+");
106         TDM_RETURN_IF_FAIL(fp != NULL);
107
108         blocks = (unsigned int *)data1;
109         fwrite(blocks, 1, size1, fp);
110
111         if (size2 > 0) {
112                 blocks = (unsigned int *)data2;
113                 fwrite(blocks, 1, size2, fp);
114         }
115
116         if (size3 > 0) {
117                 blocks = (unsigned int *)data3;
118                 fwrite(blocks, 1, size3, fp);
119         }
120
121         fclose(fp);
122 }
123
124 static void
125 _tdm_helper_dump_png(const char *file, const void *data, int width,
126                                          int height)
127 {
128         FILE *fp;
129
130         if (!_tdm_helper_check_file_is_valid(file, 1))
131                 TDM_WRN("'%s' may be symbolic link\n", file);
132
133         fp = fopen(file, "wb");
134         TDM_RETURN_IF_FAIL(fp != NULL);
135
136         png_structp pPngStruct =
137                 png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
138         if (!pPngStruct) {
139                 fclose(fp);
140                 return;
141         }
142
143         png_infop pPngInfo = png_create_info_struct(pPngStruct);
144         if (!pPngInfo) {
145                 png_destroy_write_struct(&pPngStruct, NULL);
146                 fclose(fp);
147                 return;
148         }
149
150         png_init_io(pPngStruct, fp);
151         png_set_IHDR(pPngStruct,
152                                  pPngInfo,
153                                  width,
154                                  height,
155                                  PNG_DEPTH,
156                                  PNG_COLOR_TYPE_RGBA,
157                                  PNG_INTERLACE_NONE,
158                                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
159
160         png_set_bgr(pPngStruct);
161         png_write_info(pPngStruct, pPngInfo);
162
163         const int pixel_size = 4;       // RGBA
164         png_bytep *row_pointers =
165                 png_malloc(pPngStruct, height * sizeof(png_byte *));
166         if (!row_pointers) {
167                 png_destroy_write_struct(&pPngStruct, &pPngInfo);
168                 fclose(fp);
169                 return;
170         }
171
172         unsigned int *blocks = (unsigned int *)data;
173         int y = 0;
174         int x = 0;
175
176         for (; y < height; ++y) {
177                 png_bytep row =
178                         png_malloc(pPngStruct, sizeof(png_byte) * width * pixel_size);
179                 if (!row) {
180                         for (x = 0; x < y; x++)
181                                 png_free(pPngStruct, row_pointers[x]);
182                         png_free(pPngStruct, row_pointers);
183                         png_destroy_write_struct(&pPngStruct, &pPngInfo);
184                         fclose(fp);
185                         return;
186                 }
187
188                 row_pointers[y] = (png_bytep)row;
189                 for (x = 0; x < width; ++x) {
190                         unsigned int curBlock = blocks[y * width + x];
191                         row[x * pixel_size] = (curBlock & 0xFF);
192                         row[1 + x * pixel_size] = (curBlock >> 8) & 0xFF;
193                         row[2 + x * pixel_size] = (curBlock >> 16) & 0xFF;
194                         row[3 + x * pixel_size] = (curBlock >> 24) & 0xFF;
195                 }
196         }
197
198         png_write_image(pPngStruct, row_pointers);
199         png_write_end(pPngStruct, pPngInfo);
200
201         for (y = 0; y < height; y++)
202                 png_free(pPngStruct, row_pointers[y]);
203         png_free(pPngStruct, row_pointers);
204
205         png_destroy_write_struct(&pPngStruct, &pPngInfo);
206
207         fclose(fp);
208 }
209
210 /* LCOV_EXCL_START */
211 INTERN char *
212 tdm_helper_dump_make_directory(const char *path, char *reply, int *len)
213 {
214         char *fullpath = NULL;
215         time_t timer;
216         struct tm *t, *buf = NULL;
217
218         timer = time(NULL);
219
220         buf = calloc(1, sizeof(struct tm));
221         TDM_GOTO_IF_FAIL(buf != NULL, failed_make);
222
223         fullpath = calloc(1, TDM_PATH_LEN * sizeof(char));
224         TDM_GOTO_IF_FAIL(fullpath != NULL, failed_make);
225
226         t = localtime_r(&timer, buf);
227         TDM_GOTO_IF_FAIL(t != NULL, failed_make);
228
229         snprintf(fullpath, TDM_PATH_LEN, "%s/dump_%04d%02d%02d.%02d%02d%02d", path,
230                          t->tm_year + 1900, t->tm_mon + 1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
231
232         if ((mkdir(fullpath, 0755)) < 0) {
233                 TDM_ERR("mkdir '%s' fail\n", fullpath);
234                 TDM_SNPRINTF(reply, len, "mkdir '%s' fail\n", fullpath);
235                 goto failed_make;
236         }
237
238         free(buf);
239
240         return fullpath;
241 failed_make:
242         if (fullpath)
243                 free(fullpath);
244         if (buf)
245                 free(buf);
246         return NULL;
247 }
248
249 INTERN void
250 tdm_helper_dump_buffer_str(tbm_surface_h buffer, char *dir, char *str)
251 {
252         tbm_surface_info_s info;
253         const char *ext;
254         char file[TDM_PATH_LEN];
255         int ret, bw, bh;
256
257         TDM_RETURN_IF_FAIL(buffer != NULL);
258         TDM_RETURN_IF_FAIL(dir != NULL);
259         TDM_RETURN_IF_FAIL(str != NULL);
260
261         ret = tbm_surface_get_info(buffer, &info);
262         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
263
264         if (IS_RGB(info.format))
265                 ext = file_exts[0];
266         else
267                 ext = file_exts[1];
268
269         tdm_helper_get_buffer_full_size(buffer, &bw, &bh);
270
271         snprintf(file, TDM_PATH_LEN, "%s/%c%c%c%c_%dx%d_%dx%d_%s.%s",
272                          dir, FOURCC_STR(info.format), bw, bh, info.width, info.height, str, ext);
273
274         tdm_helper_dump_buffer(buffer, file);
275 }
276 /* LCOV_EXCL_STOP */
277
278 EXTERN void
279 tdm_helper_dump_buffer(tbm_surface_h buffer, const char *file)
280 {
281         tbm_surface_info_s info;
282         int len, ret;
283         const char *ext;
284         int bo_cnt;
285         int bw, bh;
286
287         TDM_RETURN_IF_FAIL(buffer != NULL);
288         TDM_RETURN_IF_FAIL(file != NULL);
289
290         ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info);
291         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
292
293         len = strnlen(file, 1024);
294         if (IS_RGB(info.format))
295                 ext = file_exts[0];
296         else
297                 ext = file_exts[1];
298
299         if (strncmp(file + (len - 3), ext, 3)) {
300                 TDM_ERR("can't dump to '%s' file", file + (len - 3));
301                 tbm_surface_unmap(buffer);
302                 return;
303         }
304
305         tdm_helper_get_buffer_full_size(buffer, &bw, &bh);
306
307         bo_cnt = tbm_surface_internal_get_num_bos(buffer);
308         TDM_DBG("buffer: bo_cnt(%d) %dx%d(%dx%d) %c%c%c%c, plane: (%p+%d, %d,%d) (%p+%d, %d,%d) (%p+%d, %d,%d)",
309                         bo_cnt, bw, bh, info.width, info.height, FOURCC_STR(info.format),
310                         info.planes[0].ptr, info.planes[0].offset, info.planes[0].stride, info.planes[0].size,
311                         info.planes[1].ptr, info.planes[1].offset, info.planes[1].stride, info.planes[1].size,
312                         info.planes[2].ptr, info.planes[2].offset, info.planes[2].stride, info.planes[2].size);
313
314         switch (info.format) {
315         case TBM_FORMAT_ARGB8888:
316         case TBM_FORMAT_XRGB8888:
317                 _tdm_helper_dump_png(file, info.planes[0].ptr, bw, bh);
318                 break;
319         case TBM_FORMAT_YVU420:
320         case TBM_FORMAT_YUV420:
321                 _tdm_helper_dump_raw(file,
322                                                          info.planes[0].ptr,
323                                                          info.planes[0].size,
324                                                          info.planes[1].ptr,
325                                                          info.planes[1].size,
326                                                          info.planes[2].ptr,
327                                                          info.planes[2].size);
328                 break;
329         case TBM_FORMAT_NV12:
330         case TBM_FORMAT_NV21:
331                 _tdm_helper_dump_raw(file,
332                                                          info.planes[0].ptr,
333                                                          info.planes[0].size,
334                                                          info.planes[1].ptr,
335                                                          info.planes[1].size, NULL,
336                                                          0);
337                 break;
338         case TBM_FORMAT_YUYV:
339         case TBM_FORMAT_UYVY:
340                 _tdm_helper_dump_raw(file,
341                                                          info.planes[0].ptr,
342                                                          info.planes[0].size, NULL, 0,
343                                                          NULL, 0);
344                 break;
345         default:
346                 TDM_ERR("can't dump %c%c%c%c buffer", FOURCC_STR(info.format));
347                 tbm_surface_unmap(buffer);
348                 return;
349         }
350
351         tbm_surface_unmap(buffer);
352
353         TDM_INFO("dump %s", file);
354 }
355
356 EXTERN void
357 tdm_helper_clear_buffer_color(tbm_surface_h buffer, tdm_pos *pos, unsigned int color)
358 {
359         tbm_surface_info_s info;
360         int ret;
361
362         TDM_RETURN_IF_FAIL(buffer != NULL);
363
364         ret = tbm_surface_map(buffer, TBM_OPTION_READ, &info);
365         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
366
367         switch (info.format) {
368         case TBM_FORMAT_ARGB8888:
369         case TBM_FORMAT_XRGB8888:
370                 if (!pos) {
371                         memset(info.planes[0].ptr, 0, info.planes[0].stride * info.height);
372                 } else {
373                         unsigned char *p;
374                         int x, y;
375                         for (y = pos->y; y <= (pos->y + pos->h); y++) {
376                                 p = info.planes[0].ptr + info.planes[0].stride * y;
377                                 for (x = pos->x; x <= (pos->x + pos->w); x++) {
378                                         unsigned int *ibuf = (unsigned int*)p;
379                                         ibuf[x] = color;
380                                 }
381                         }
382                 }
383                 break;
384         case TBM_FORMAT_YVU420:
385         case TBM_FORMAT_YUV420:
386                 memset((char*)info.planes[0].ptr, 0x10, info.planes[0].stride * info.height);
387                 memset((char*)info.planes[1].ptr, 0x80, info.planes[1].stride * (info.height >> 1));
388                 memset((char*)info.planes[2].ptr, 0x80, info.planes[2].stride * (info.height >> 1));
389                 break;
390         case TBM_FORMAT_NV12:
391         case TBM_FORMAT_NV21:
392                 memset((char*)info.planes[0].ptr, 0x10, info.planes[0].stride * info.height);
393                 memset((char*)info.planes[1].ptr, 0x80, info.planes[1].stride * (info.height >> 1));
394                 break;
395         case TBM_FORMAT_YUYV: {
396                 int *ibuf = (int*)info.planes[0].ptr;
397                 int i, size = info.planes[0].stride * info.height / 4;
398
399                 for (i = 0 ; i < size ; i++)
400                         ibuf[i] = 0x10801080;
401         }
402         break;
403         case TBM_FORMAT_UYVY: {
404                 int *ibuf = (int*)info.planes[0].ptr;
405                 int i, size = info.planes[0].stride * info.height / 4;
406
407                 for (i = 0 ; i < size ; i++)
408                         ibuf[i] = 0x80108010; /* YUYV -> 0xVYUY */
409         }
410         break;
411         default:
412                 TDM_ERR("can't clear %c%c%c%c buffer", FOURCC_STR(info.format));
413                 break;
414         }
415
416         tbm_surface_unmap(buffer);
417 }
418
419 EXTERN void
420 tdm_helper_clear_buffer_pos(tbm_surface_h buffer, tdm_pos *pos)
421 {
422         TDM_RETURN_IF_FAIL(buffer != NULL);
423
424         tdm_helper_clear_buffer_color(buffer, pos, 0);
425 }
426
427 EXTERN void
428 tdm_helper_clear_buffer(tbm_surface_h buffer)
429 {
430         TDM_RETURN_IF_FAIL(buffer != NULL);
431
432         tdm_helper_clear_buffer_pos(buffer, NULL);
433 }
434
435 EXTERN void
436 tdm_helper_get_buffer_full_size(tbm_surface_h buffer, int *buffer_w, int *buffer_h)
437 {
438         tbm_surface_info_s info;
439         int ret;
440
441         TDM_RETURN_IF_FAIL(buffer != NULL);
442
443         ret = tbm_surface_get_info(buffer, &info);
444         TDM_RETURN_IF_FAIL(ret == TBM_SURFACE_ERROR_NONE);
445
446         if (buffer_w) {
447                 if (IS_RGB(info.format))
448                         *buffer_w = info.planes[0].stride >> 2;
449                 else
450                         *buffer_w = info.planes[0].stride;
451         }
452
453         if (buffer_h)
454                 *buffer_h = info.planes[0].size / info.planes[0].stride;
455 }
456
457 static pixman_format_code_t
458 _tdm_helper_pixman_format_get(tbm_format format)
459 {
460         switch (format) {
461         case TBM_FORMAT_ARGB8888:
462                 return PIXMAN_a8r8g8b8;
463         case TBM_FORMAT_XRGB8888:
464                 return PIXMAN_x8r8g8b8;
465         default:
466                 return 0;
467         }
468
469         return 0;
470 }
471
472 EXTERN tdm_error
473 tdm_helper_convert_buffer(tbm_surface_h srcbuf, tbm_surface_h dstbuf,
474                                                   tdm_pos *srcpos, tdm_pos *dstpos,
475                                                   tdm_transform transform, int over)
476 {
477         tbm_surface_info_s src_info, dst_info;
478         pixman_image_t *src_img = NULL, *dst_img = NULL;
479         pixman_format_code_t src_format, dst_format;
480         double scale_x, scale_y;
481         int rotate_step, bos;
482         pixman_transform_t t;
483         struct pixman_f_transform ft;
484         pixman_op_t op;
485         int src_stride, dst_stride;
486         int buf_width, err;
487         tdm_error ret = TDM_ERROR_OPERATION_FAILED;
488
489         TDM_RETURN_VAL_IF_FAIL(srcbuf != NULL, TDM_ERROR_INVALID_PARAMETER);
490         TDM_RETURN_VAL_IF_FAIL(dstbuf != NULL, TDM_ERROR_INVALID_PARAMETER);
491
492         bos = tbm_surface_internal_get_num_bos(srcbuf);
493         TDM_RETURN_VAL_IF_FAIL(bos == 1, TDM_ERROR_OPERATION_FAILED);
494
495         bos = tbm_surface_internal_get_num_bos(dstbuf);
496         TDM_RETURN_VAL_IF_FAIL(bos == 1, TDM_ERROR_OPERATION_FAILED);
497
498         err = tbm_surface_map(srcbuf, TBM_OPTION_READ, &src_info);
499         TDM_RETURN_VAL_IF_FAIL(err == TBM_SURFACE_ERROR_NONE, TDM_ERROR_OPERATION_FAILED);
500
501         err = tbm_surface_map(dstbuf, TBM_OPTION_WRITE, &dst_info);
502         TDM_GOTO_IF_FAIL(err == TBM_SURFACE_ERROR_NONE, unmap_srcbuf);
503
504         /* not handle buffers which have 2 more gem handles */
505         TDM_GOTO_IF_FAIL(src_info.planes[0].ptr != NULL, unmap_dstbuf);
506         TDM_GOTO_IF_FAIL(dst_info.planes[0].ptr != NULL, unmap_dstbuf);
507
508         src_format = _tdm_helper_pixman_format_get(src_info.format);
509         TDM_GOTO_IF_FAIL(src_format > 0, unmap_dstbuf);
510         dst_format = _tdm_helper_pixman_format_get(dst_info.format);
511         TDM_GOTO_IF_FAIL(dst_format > 0, unmap_dstbuf);
512
513         buf_width = src_info.planes[0].stride >> 2;
514         src_stride = src_info.planes[0].stride;
515         src_img = pixman_image_create_bits(src_format, buf_width, src_info.height,
516                                                                            (uint32_t*)src_info.planes[0].ptr, src_stride);
517         TDM_GOTO_IF_FAIL(src_img, unref_img);
518
519         buf_width = dst_info.planes[0].stride >> 2;
520         dst_stride = dst_info.planes[0].stride;
521         dst_img = pixman_image_create_bits(dst_format, buf_width, dst_info.height,
522                                                                            (uint32_t*)dst_info.planes[0].ptr, dst_stride);
523         TDM_GOTO_IF_FAIL(dst_img, unref_img);
524
525         pixman_f_transform_init_identity(&ft);
526
527         if (transform & TDM_TRANSFORM_FLIPPED) {
528                 pixman_f_transform_scale(&ft, NULL, -1, 1);
529                 pixman_f_transform_translate(&ft, NULL, dstpos->w, 0);
530         }
531
532         rotate_step = transform & 0x3;
533         if (rotate_step > 0) {
534                 int c, s, tx = 0, ty = 0;
535                 switch (rotate_step) {
536                 case 1:
537                         c = 0, s = -1, tx = -dstpos->w;
538                         break;
539                 case 2:
540                         c = -1, s = 0, tx = -dstpos->w, ty = -dstpos->h;
541                         break;
542                 case 3:
543                         c = 0, s = 1, ty = -dstpos->h;
544                         break;
545                 }
546                 pixman_f_transform_translate(&ft, NULL, tx, ty);
547                 pixman_f_transform_rotate(&ft, NULL, c, s);
548         }
549
550         if (rotate_step % 2 == 0) {
551                 scale_x = (double)srcpos->w / dstpos->w;
552                 scale_y = (double)srcpos->h / dstpos->h;
553         } else {
554                 scale_x = (double)srcpos->w / dstpos->h;
555                 scale_y = (double)srcpos->h / dstpos->w;
556         }
557
558         pixman_f_transform_scale(&ft, NULL, scale_x, scale_y);
559         pixman_f_transform_translate(&ft, NULL, srcpos->x, srcpos->y);
560         pixman_transform_from_pixman_f_transform(&t, &ft);
561         pixman_image_set_transform(src_img, &t);
562
563         op = (!over) ? PIXMAN_OP_SRC : PIXMAN_OP_OVER;
564
565         pixman_image_composite(op, src_img, NULL, dst_img, 0, 0, 0, 0,
566                                                    dstpos->x, dstpos->y, dstpos->w, dstpos->h);
567
568         ret = TDM_ERROR_NONE;
569
570 unref_img:
571         if (src_img)
572                 pixman_image_unref(src_img);
573         if (dst_img)
574                 pixman_image_unref(dst_img);
575 unmap_dstbuf:
576         tbm_surface_unmap(dstbuf);
577 unmap_srcbuf:
578         tbm_surface_unmap(srcbuf);
579
580         return ret;
581 }
582
583 /* LCOV_EXCL_START */
584 EXTERN int
585 tdm_helper_get_fd(const char *env)
586 {
587         if (strncmp(env, "TBM_DRM_MASTER_FD", 17) && strncmp(env, "TDM_DRM_MASTER_FD", 17)) {
588                 TDM_INFO("DEPRECATED! '%s'", env);
589                 return -1;
590         }
591
592         return tbm_drm_helper_get_master_fd();
593 }
594
595 EXTERN void
596 tdm_helper_set_fd(const char *env, int fd)
597 {
598         if (strncmp(env, "TBM_DRM_MASTER_FD", 17) && strncmp(env, "TDM_DRM_MASTER_FD", 17)) {
599                 TDM_INFO("DEPRECATED! '%s'", env);
600                 return;
601         }
602
603         tbm_drm_helper_set_tbm_master_fd(fd);
604 }
605 /* LCOV_EXCL_STOP */
606
607 EXTERN void
608 tdm_helper_dump_start(char *dumppath, int *count)
609 {
610         if (dumppath == NULL || count == NULL) {
611                 TDM_DBG("tdm_helper_dump dumppath or count is null.");
612                 return;
613         }
614
615         tdm_dump_enable = 1;
616
617         TDM_DBG("tdm_helper_dump start.(path : %s)", dumppath);
618 }
619
620 EXTERN void
621 tdm_helper_dump_stop(void)
622 {
623         tdm_dump_enable = 0;
624
625         TDM_DBG("tdm_helper_dump stop.");
626 }
627
628 /* LCOV_EXCL_START */
629 static tdm_error
630 _tdm_helper_buffer_convert(tbm_surface_h srcbuf, tbm_surface_h dstbuf,
631                                                    int dx, int dy, int dw, int dh, int count)
632 {
633         pixman_image_t *src_img = NULL, *dst_img = NULL;
634         pixman_format_code_t src_format, dst_format;
635         pixman_transform_t t;
636         struct pixman_f_transform ft;
637         pixman_op_t op;
638         tbm_surface_info_s src_info = {0, };
639         tbm_surface_info_s dst_info = {0, };
640         int stride, width;
641         double scale_x, scale_y;
642
643         TDM_RETURN_VAL_IF_FAIL(srcbuf != NULL, TDM_ERROR_INVALID_PARAMETER);
644         TDM_RETURN_VAL_IF_FAIL(dstbuf != NULL, TDM_ERROR_INVALID_PARAMETER);
645
646         if (tbm_surface_map(srcbuf, TBM_SURF_OPTION_READ, &src_info) != TBM_SURFACE_ERROR_NONE) {
647                 TDM_ERR("cannot mmap srcbuf\n");
648                 return TDM_ERROR_OPERATION_FAILED;
649         }
650
651         if (tbm_surface_map(dstbuf, TBM_SURF_OPTION_WRITE, &dst_info) != TBM_SURFACE_ERROR_NONE) {
652                 TDM_ERR("cannot mmap dstbuf\n");
653                 tbm_surface_unmap(srcbuf);
654                 return TDM_ERROR_OPERATION_FAILED;
655         }
656         TDM_GOTO_IF_FAIL(src_info.num_planes == 1, cant_convert);
657         TDM_GOTO_IF_FAIL(dst_info.num_planes == 1, cant_convert);
658
659         /* src */
660         src_format = _tdm_helper_pixman_format_get(src_info.format);
661         TDM_GOTO_IF_FAIL(src_format > 0, cant_convert);
662
663         width = src_info.planes[0].stride / 4;
664         stride = src_info.planes[0].stride;
665         src_img = pixman_image_create_bits(src_format, width, src_info.height,
666                                                                            (uint32_t*)src_info.planes[0].ptr, stride);
667         TDM_GOTO_IF_FAIL(src_img != NULL, cant_convert);
668
669         /* dst */
670         dst_format = _tdm_helper_pixman_format_get(dst_info.format);
671         TDM_GOTO_IF_FAIL(dst_format > 0, cant_convert);
672
673         width = dst_info.planes[0].stride / 4;
674         stride = dst_info.planes[0].stride;
675         dst_img = pixman_image_create_bits(dst_format, width, dst_info.height,
676                                                                            (uint32_t*)dst_info.planes[0].ptr, stride);
677         TDM_GOTO_IF_FAIL(dst_img != NULL, cant_convert);
678
679         pixman_f_transform_init_identity(&ft);
680
681         scale_x = (double)src_info.width / dw;
682         scale_y = (double)src_info.height / dh;
683
684         pixman_f_transform_scale(&ft, NULL, scale_x, scale_y);
685         pixman_f_transform_translate(&ft, NULL, 0, 0);
686         pixman_transform_from_pixman_f_transform(&t, &ft);
687         pixman_image_set_transform(src_img, &t);
688
689         if (count == 0)
690                 op = PIXMAN_OP_SRC;
691         else
692                 op = PIXMAN_OP_OVER;
693
694         pixman_image_composite(op, src_img, NULL, dst_img,
695                                                    0, 0, 0, 0, dx, dy, dw, dh);
696
697         if (src_img)
698                 pixman_image_unref(src_img);
699         if (dst_img)
700                 pixman_image_unref(dst_img);
701
702         tbm_surface_unmap(srcbuf);
703         tbm_surface_unmap(dstbuf);
704
705         return TDM_ERROR_NONE;
706
707 cant_convert:
708         if (src_img)
709                 pixman_image_unref(src_img);
710
711         tbm_surface_unmap(srcbuf);
712         tbm_surface_unmap(dstbuf);
713
714         return TDM_ERROR_OPERATION_FAILED;
715 }
716 /* LCOV_EXCL_STOP */
717
718
719 EXTERN tdm_error
720 tdm_helper_capture_output(tdm_output *output, tbm_surface_h dst_buffer,
721                                                   int x, int y, int w, int h,
722                                                   tdm_helper_capture_handler func, void *data)
723 {
724         tbm_surface_h surface;
725         tdm_error err;
726         int i, count, first = 0;
727
728         TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
729         TDM_RETURN_VAL_IF_FAIL(dst_buffer != NULL, TDM_ERROR_INVALID_PARAMETER);
730         TDM_RETURN_VAL_IF_FAIL(x >= 0, TDM_ERROR_INVALID_PARAMETER);
731         TDM_RETURN_VAL_IF_FAIL(y >= 0, TDM_ERROR_INVALID_PARAMETER);
732         TDM_RETURN_VAL_IF_FAIL(w >= 0, TDM_ERROR_INVALID_PARAMETER);
733         TDM_RETURN_VAL_IF_FAIL(h >= 0, TDM_ERROR_INVALID_PARAMETER);
734         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
735         TDM_RETURN_VAL_IF_FAIL(data != NULL, TDM_ERROR_INVALID_PARAMETER);
736
737         err = tdm_output_get_layer_count(output, &count);
738         if (err != TDM_ERROR_NONE) {
739                 TDM_ERR("tdm_output_get_layer_count fail(%d)\n", err);
740                 return TDM_ERROR_OPERATION_FAILED;
741         }
742         if (count <= 0) {
743                 TDM_ERR("tdm_output_get_layer_count err(%d, %d)\n", err, count);
744                 return TDM_ERROR_BAD_MODULE;
745         }
746
747         for (i = count - 1; i >= 0; i--) {
748                 tdm_layer *layer = tdm_output_get_layer(output, i, NULL);
749
750                 surface = tdm_layer_get_displaying_buffer(layer, &err);
751                 if (err != TDM_ERROR_NONE)
752                         continue;
753
754                 err = _tdm_helper_buffer_convert(surface, dst_buffer, x, y, w, h, first++);
755                 if (err != TDM_ERROR_NONE)
756                         TDM_DBG("convert fail %d-layer buffer\n", i);
757                 else
758                         TDM_DBG("convert success %d-layer buffer\n", i);
759         }
760
761         func(dst_buffer, data);
762
763         return TDM_ERROR_NONE;
764 }
765
766 EXTERN void
767 tdm_helper_get_display_information(tdm_display *dpy, char *reply, int *len)
768 {
769         tdm_private_display *private_display;
770         tdm_backend_module *module_data;
771         tdm_func_output *func_output;
772         tdm_func_layer *func_layer;
773         tdm_private_output *private_output = NULL;
774         tdm_private_layer *private_layer = NULL;
775         tdm_private_pp *private_pp = NULL;
776         tdm_private_capture *private_capture = NULL;
777         tdm_error ret;
778         int i;
779
780         TDM_DBG_RETURN_IF_FAIL(dpy != NULL);
781
782         private_display = dpy;
783         func_output = &private_display->func_output;
784         func_layer = &private_display->func_layer;
785
786         /* module information */
787         module_data = private_display->module_data;
788         TDM_SNPRINTF(reply, len, "[TDM backend information]\n");
789         TDM_SNPRINTF(reply, len, "name: %s\n", module_data->name);
790         TDM_SNPRINTF(reply, len, "vendor: %s\n", module_data->vendor);
791         TDM_SNPRINTF(reply, len, "version: %d.%d\n\n",
792                                  (int)TDM_BACKEND_GET_ABI_MAJOR(module_data->abi_version),
793                                  (int)TDM_BACKEND_GET_ABI_MINOR(module_data->abi_version));
794
795         /* output information */
796         TDM_SNPRINTF(reply, len, "[Output information]\n");
797         TDM_SNPRINTF(reply, len, "--------------------------------------------------------------------------------------------\n");
798         TDM_SNPRINTF(reply, len, "idx   maker   model   name   type   status   dpms   subpixel   align_w   min   max   phy(mm)\n");
799         TDM_SNPRINTF(reply, len, "--------------------------------------------------------------------------------------------\n");
800         LIST_FOR_EACH_ENTRY(private_output, &private_display->output_list, link) {
801                 TDM_SNPRINTF(reply, len, "%d   %s   %s   %s   %s   %s   %s   %u   %d   %dx%d   %dx%d   %ux%u\n",
802                                          private_output->index, private_output->caps.maker,
803                                          private_output->caps.model, private_output->caps.name,
804                                          tdm_conn_str(private_output->caps.type),
805                                          tdm_status_str(private_output->caps.status),
806                                          tdm_dpms_str(private_output->current_dpms_value),
807                                          private_output->caps.subpixel,
808                                          TDM_FRONT_VALUE(private_output->caps.preferred_align),
809                                          TDM_FRONT_VALUE(private_output->caps.min_w),
810                                          TDM_FRONT_VALUE(private_output->caps.min_h),
811                                          TDM_FRONT_VALUE(private_output->caps.max_w),
812                                          TDM_FRONT_VALUE(private_output->caps.max_h),
813                                          private_output->caps.mmWidth, private_output->caps.mmHeight);
814
815                 TDM_SNPRINTF(reply, len, "\t%u modes:\n", private_output->caps.mode_count);
816
817                 if (private_output->caps.mode_count > 0) {
818                         const tdm_output_mode *current_mode = NULL;
819
820                         TDM_DBG_RETURN_IF_FAIL(func_output->output_get_mode);
821                         ret = func_output->output_get_mode(private_output->output_backend, &current_mode);
822                         TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
823
824                         TDM_SNPRINTF(reply, len, "\t\t name refresh (Hz) clk hdisp hss hse htot vdisp vss vse vtot vscan\n");
825                         for (i = 0; i < private_output->caps.mode_count; i++) {
826                                 char *current = (current_mode == private_output->caps.modes + i) ? "*" : " ";
827                                 TDM_SNPRINTF(reply, len, "\t\t%s%s %u %u %u %u %u %u %u %u %u %u %u ",
828                                                          current,
829                                                          private_output->caps.modes[i].name,
830                                                          private_output->caps.modes[i].vrefresh,
831                                                          private_output->caps.modes[i].clock,
832                                                          private_output->caps.modes[i].hdisplay,
833                                                          private_output->caps.modes[i].hsync_start,
834                                                          private_output->caps.modes[i].hsync_end,
835                                                          private_output->caps.modes[i].htotal,
836                                                          private_output->caps.modes[i].vdisplay,
837                                                          private_output->caps.modes[i].vsync_start,
838                                                          private_output->caps.modes[i].vsync_end,
839                                                          private_output->caps.modes[i].vtotal,
840                                                          private_output->caps.modes[i].vscan);
841                                 tdm_mode_flag_str(private_output->caps.modes[i].flags, &reply, len);
842                                 TDM_SNPRINTF(reply, len, " ");
843                                 tdm_mode_type_str(private_output->caps.modes[i].type, &reply, len);
844                                 TDM_SNPRINTF(reply, len, "\n");
845                         }
846                 }
847
848                 TDM_SNPRINTF(reply, len, "\t%d properties:\n", private_output->caps.prop_count);
849                 if (private_output->caps.prop_count > 0) {
850                         TDM_SNPRINTF(reply, len, "\t\tname\ttype\tidx\tvalue\n");
851                         for (i = 0; i < private_output->caps.prop_count; i++) {
852                                 tdm_value value;
853                                 TDM_DBG_RETURN_IF_FAIL(func_output->output_get_property);
854                                 ret = func_output->output_get_property(private_output->output_backend,
855                                                                                                            private_output->caps.props[i].id,
856                                                                                                            &value);
857                                 TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
858                                 TDM_SNPRINTF(reply, len, "\t\t%s\t%s\t%u\t",
859                                                          private_output->caps.props[i].name,
860                                                          tdm_value_type_str(private_output->caps.props[i].type),
861                                                          private_output->caps.props[i].id);
862                                 switch (private_output->caps.props[i].type) {
863                                 case TDM_VALUE_TYPE_PTR:
864                                         TDM_SNPRINTF(reply, len, "%p\n", value.ptr);
865                                         break;
866                                 case TDM_VALUE_TYPE_INT32:
867                                         TDM_SNPRINTF(reply, len, "%d\n", value.s32);
868                                         break;
869                                 case TDM_VALUE_TYPE_INT64:
870                                         TDM_SNPRINTF(reply, len, "%"PRId64"\n", value.s64);
871                                         break;
872                                 case TDM_VALUE_TYPE_UINT64:
873                                         TDM_SNPRINTF(reply, len, "%"PRIu64"\n", value.u64);
874                                         break;
875                                 case TDM_VALUE_TYPE_UINT32:
876                                 default:
877                                         TDM_SNPRINTF(reply, len, "%u\n", value.u32);
878                                         break;
879                                 }
880                         }
881                 }
882         }
883         TDM_SNPRINTF(reply, len, "\n");
884
885         /* layer information */
886         TDM_SNPRINTF(reply, len, "[Layer information]\n");
887         TDM_SNPRINTF(reply, len, "-----------------------------------------------------------------------\n");
888         TDM_SNPRINTF(reply, len, "idx   output   zpos   buf   format   size   crop   geometry   transform\n");
889         TDM_SNPRINTF(reply, len, "-----------------------------------------------------------------------\n");
890         LIST_FOR_EACH_ENTRY(private_output, &private_display->output_list, link) {
891                 LIST_FOR_EACH_ENTRY(private_layer, &private_output->layer_list, link) {
892                         if (!private_layer->usable) {
893                                 tdm_info_layer info;
894                                 unsigned int format;
895                                 tdm_size size;
896                                 tbm_surface_info_s buf_info;
897
898                                 TDM_DBG_RETURN_IF_FAIL(func_layer->layer_get_info);
899                                 memset(&info, 0, sizeof info);
900                                 ret = func_layer->layer_get_info(private_layer->layer_backend, &info);
901                                 TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
902
903                                 if (!private_layer->showing_buffer)
904                                         continue;
905
906                                 format = tbm_surface_get_format(private_layer->showing_buffer->buffer);
907                                 tbm_surface_get_info(private_layer->showing_buffer->buffer, &buf_info);
908
909                                 if (IS_RGB(format))
910                                         size.h = buf_info.planes[0].stride >> 2;
911                                 else
912                                         size.h = buf_info.planes[0].stride;
913                                 size.v = tbm_surface_get_height(private_layer->showing_buffer->buffer);
914
915                                 if (info.src_config.format)
916                                         format = (info.src_config.format) ? : format;
917
918                                 TDM_SNPRINTF(reply, len, "%d   %d   %d   %p   %c%c%c%c   %ux%u   %ux%u+%u+%u   %ux%u+%u+%u   %s\n",
919                                                          private_layer->index,
920                                                          private_output->index,
921                                                          private_layer->caps.zpos,
922                                                          private_layer->showing_buffer->buffer, FOURCC_STR(format), size.h, size.v,
923                                                          info.src_config.pos.w, info.src_config.pos.h, info.src_config.pos.x, info.src_config.pos.y,
924                                                          info.dst_pos.w, info.dst_pos.h, info.dst_pos.x, info.dst_pos.y,
925                                                          tdm_transform_str(info.transform));
926                         } else {
927                                 TDM_SNPRINTF(reply, len, "%d   %d   %d   -\n",
928                                                          private_layer->index,
929                                                          private_output->index,
930                                                          private_layer->caps.zpos);
931                         }
932
933                         TDM_SNPRINTF(reply, len, "\tcaps\t: ");
934                         tdm_layer_caps_str(private_layer->caps.capabilities, &reply, len);
935                         TDM_SNPRINTF(reply, len, "\n");
936
937                         TDM_SNPRINTF(reply, len, "\tformats\t: ");
938                         if (private_layer->caps.format_count > 0) {
939                                 const char *sep = "";
940                                 for (i = 0; i < private_layer->caps.format_count; i++) {
941                                         if (private_layer->caps.formats[i] == 0)
942                                                 continue;
943                                         TDM_SNPRINTF(reply, len, "%s%c%c%c%c", sep, FOURCC_STR(private_layer->caps.formats[i]));
944                                         sep = ",";
945                                 }
946                                 TDM_SNPRINTF(reply, len, "\n");
947                         }
948
949                         TDM_SNPRINTF(reply, len, "\t%u properties:\n", private_layer->caps.prop_count);
950                         if (private_layer->caps.prop_count > 0) {
951                                 TDM_SNPRINTF(reply, len, "\t\tname\ttype\tidx\tvalue\n");
952                                 for (i = 0; i < private_layer->caps.prop_count; i++) {
953                                         tdm_value value;
954                                         TDM_DBG_RETURN_IF_FAIL(func_layer->layer_get_property);
955                                         ret = func_layer->layer_get_property(private_layer->layer_backend,
956                                                                                                                  private_layer->caps.props[i].id,
957                                                                                                                  &value);
958                                         TDM_DBG_RETURN_IF_FAIL(ret == TDM_ERROR_NONE);
959                                         TDM_SNPRINTF(reply, len, "\t\t%s\t%s\t%u\t",
960                                                                  private_layer->caps.props[i].name,
961                                                                  tdm_value_type_str(private_output->caps.props[i].type),
962                                                                  private_layer->caps.props[i].id);
963                                 switch (private_layer->caps.props[i].type) {
964                                 case TDM_VALUE_TYPE_PTR:
965                                         TDM_SNPRINTF(reply, len, "%p\n", value.ptr);
966                                         break;
967                                 case TDM_VALUE_TYPE_INT32:
968                                         TDM_SNPRINTF(reply, len, "%d\n", value.s32);
969                                         break;
970                                 case TDM_VALUE_TYPE_INT64:
971                                         TDM_SNPRINTF(reply, len, "%"PRId64"\n", value.s64);
972                                         break;
973                                 case TDM_VALUE_TYPE_UINT64:
974                                         TDM_SNPRINTF(reply, len, "%"PRIu64"\n", value.u64);
975                                         break;
976                                 case TDM_VALUE_TYPE_UINT32:
977                                 default:
978                                         TDM_SNPRINTF(reply, len, "%u\n", value.u32);
979                                         break;
980                                 }
981                                 }
982                         }
983                 }
984         }
985         TDM_SNPRINTF(reply, len, "\n");
986
987         if (private_display->capabilities & TDM_DISPLAY_CAPABILITY_PP) {
988                 const char *sep = "";
989                 TDM_SNPRINTF(reply, len, "[PP information]\n");
990                 TDM_SNPRINTF(reply, len, "caps\t: ");
991                 tdm_pp_caps_str(private_display->caps_pp.capabilities, &reply, len);
992                 TDM_SNPRINTF(reply, len, "\n");
993                 TDM_SNPRINTF(reply, len, "formats\t: ");
994                 for (i = 0; i < private_display->caps_pp.format_count; i++) {
995                         if (private_display->caps_pp.formats[i] == 0)
996                                 continue;
997                         TDM_SNPRINTF(reply, len, "%s%c%c%c%c", sep, FOURCC_STR(private_display->caps_pp.formats[i]));
998                         sep = ",";
999                 }
1000                 TDM_SNPRINTF(reply, len, "\n");
1001                 TDM_SNPRINTF(reply, len, "size\t: min(%dx%d) max(%dx%d) align_w(%d)\n",
1002                                          TDM_FRONT_VALUE(private_display->caps_pp.min_w),
1003                                          TDM_FRONT_VALUE(private_display->caps_pp.min_h),
1004                                          TDM_FRONT_VALUE(private_display->caps_pp.max_w),
1005                                          TDM_FRONT_VALUE(private_display->caps_pp.max_h),
1006                                          TDM_FRONT_VALUE(private_display->caps_pp.preferred_align));
1007                 if (!LIST_IS_EMPTY(&private_display->pp_list)) {
1008                         TDM_SNPRINTF(reply, len, "-------------------------------------------------------------\n");
1009                         TDM_SNPRINTF(reply, len, "src(format size crop)  |  dst(format size crop)  |  transform\n");
1010                         TDM_SNPRINTF(reply, len, "-------------------------------------------------------------\n");
1011                         LIST_FOR_EACH_ENTRY(private_pp, &private_display->pp_list, link) {
1012                                 TDM_SNPRINTF(reply, len, "%c%c%c%c %ux%u %ux%u+%u+%u | %c%c%c%c %ux%u %ux%u+%u+%u | %s\n",
1013                                                          FOURCC_STR(private_pp->info.src_config.format),
1014                                                          private_pp->info.src_config.size.h,
1015                                                          private_pp->info.src_config.size.v,
1016                                                          private_pp->info.src_config.pos.x, private_pp->info.src_config.pos.y,
1017                                                          private_pp->info.src_config.pos.w, private_pp->info.src_config.pos.h,
1018                                                          FOURCC_STR(private_pp->info.dst_config.format),
1019                                                          private_pp->info.dst_config.size.h,
1020                                                          private_pp->info.dst_config.size.v,
1021                                                          private_pp->info.dst_config.pos.x, private_pp->info.dst_config.pos.y,
1022                                                          private_pp->info.dst_config.pos.w, private_pp->info.dst_config.pos.h,
1023                                                          tdm_transform_str(private_pp->info.transform));
1024                         }
1025                 }
1026         } else {
1027                 TDM_SNPRINTF(reply, len, "[No PP capability]\n");
1028         }
1029         TDM_SNPRINTF(reply, len, "\n");
1030
1031         if (private_display->capabilities & TDM_DISPLAY_CAPABILITY_CAPTURE) {
1032                 const char *sep = "";
1033                 TDM_SNPRINTF(reply, len, "[Capture information]\n");
1034                 TDM_SNPRINTF(reply, len, "caps\t: ");
1035                 tdm_capture_caps_str(private_display->caps_capture.capabilities, &reply, len);
1036                 TDM_SNPRINTF(reply, len, "\n");
1037                 TDM_SNPRINTF(reply, len, "formats\t: ");
1038                 for (i = 0; i < private_display->caps_capture.format_count; i++) {
1039                         if (private_display->caps_capture.formats[i] == 0)
1040                                 continue;
1041                         TDM_SNPRINTF(reply, len, "%s%c%c%c%c", sep, FOURCC_STR(private_display->caps_capture.formats[i]));
1042                         sep = ",";
1043                 }
1044                 TDM_SNPRINTF(reply, len, "\n");
1045                 TDM_SNPRINTF(reply, len, "size\t: min(%dx%d) max(%dx%d) align_w(%d)\n",
1046                                          TDM_FRONT_VALUE(private_display->caps_capture.min_w),
1047                                          TDM_FRONT_VALUE(private_display->caps_capture.min_h),
1048                                          TDM_FRONT_VALUE(private_display->caps_capture.max_w),
1049                                          TDM_FRONT_VALUE(private_display->caps_capture.max_h),
1050                                          TDM_FRONT_VALUE(private_display->caps_capture.preferred_align));
1051                 if (!LIST_IS_EMPTY(&private_display->capture_list)) {
1052                         TDM_SNPRINTF(reply, len, "-----------------------------------\n");
1053                         TDM_SNPRINTF(reply, len, "dst(format size crop)  |  transform\n");
1054                         TDM_SNPRINTF(reply, len, "-----------------------------------\n");
1055                         LIST_FOR_EACH_ENTRY(private_capture, &private_display->capture_list, link) {
1056                                 TDM_SNPRINTF(reply, len, "%c%c%c%c %ux%u %ux%u+%u+%u | %s\n",
1057                                                          FOURCC_STR(private_capture->info.dst_config.format),
1058                                                          private_capture->info.dst_config.size.h,
1059                                                          private_capture->info.dst_config.size.v,
1060                                                          private_capture->info.dst_config.pos.x, private_capture->info.dst_config.pos.y,
1061                                                          private_capture->info.dst_config.pos.w, private_capture->info.dst_config.pos.h,
1062                                                          tdm_transform_str(private_capture->info.transform));
1063                         }
1064                 }
1065         } else {
1066                 TDM_SNPRINTF(reply, len, "[No Capture capability]\n");
1067         }
1068         TDM_SNPRINTF(reply, len, "\n");
1069 }
1070
1071 /* LCOV_EXCL_START */
1072 EXTERN int
1073 tdm_helper_commit_per_vblank_enabled(tdm_display *dpy)
1074 {
1075         TDM_ERR("the deprecated function, use 'tdm_helper_output_commit_per_vblank_enabled' instead.");
1076
1077         return 0;
1078 }
1079 /* LCOV_EXCL_STOP */
1080
1081 EXTERN int
1082 tdm_helper_output_commit_per_vblank_enabled(tdm_output *output)
1083 {
1084         tdm_private_output *private_output = output;
1085
1086         TDM_RETURN_VAL_IF_FAIL(private_output != NULL, -1);
1087
1088         return !!private_output->commit_per_vblank;
1089 }
1090