output: add thread_cb even if func is null
[platform/core/uifw/libtdm.git] / src / tdm_pp.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 "tdm_private.h"
41
42 #define PP_FUNC_ENTRY() \
43         tdm_private_module *private_module; \
44         tdm_func_pp *func_pp; \
45         tdm_private_display *private_display; \
46         tdm_private_pp *private_pp; \
47         tdm_error ret = TDM_ERROR_NONE; \
48         TDM_RETURN_VAL_IF_FAIL(pp != NULL, TDM_ERROR_INVALID_PARAMETER); \
49         private_pp = (tdm_private_pp*)pp; \
50         private_display = private_pp->private_display; \
51         private_module = private_pp->private_module; \
52         func_pp = &private_module->func_pp
53
54 static void
55 _tdm_pp_print_list(struct list_head *list)
56 {
57         tdm_pp_private_buffer *b = NULL;
58         char str[512], *p;
59         int len = sizeof(str);
60
61         TDM_RETURN_IF_FAIL(list != NULL);
62
63         p = str;
64         LIST_FOR_EACH_ENTRY(b, list, link) {
65                 if (len > 0) {
66                         int l = snprintf(p, len, " (%p,%p)", b->src, b->dst);
67                         p += l;
68                         len -= l;
69                 } else
70                         break;
71         }
72
73         TDM_INFO("\t %s", str);
74 }
75
76 static tdm_pp_private_buffer *
77 _tdm_pp_find_tbm_buffers(struct list_head *list, tbm_surface_h src, tbm_surface_h dst)
78 {
79         tdm_pp_private_buffer *b = NULL, *bb = NULL;
80
81         LIST_FOR_EACH_ENTRY_SAFE(b, bb, list, link) {
82                 if (b->src == src && b->dst == dst)
83                         return b;
84         }
85
86         return NULL;
87 }
88
89 static tdm_pp_private_buffer *
90 _tdm_pp_find_buffer(struct list_head *list, tdm_pp_private_buffer *pp_buffer)
91 {
92         tdm_pp_private_buffer *b = NULL, *bb = NULL;
93
94         LIST_FOR_EACH_ENTRY_SAFE(b, bb, list, link) {
95                 if (b == pp_buffer)
96                         return b;
97         }
98
99         return NULL;
100 }
101
102 static void
103 _tdm_pp_thread_cb_done(tdm_private_display *private_display, void *object, tdm_thread_cb_base *cb_base, void *user_data)
104 {
105         tdm_thread_cb_pp_done *pp_done = (tdm_thread_cb_pp_done *)cb_base;
106         tdm_private_pp *private_pp = object;
107         tdm_pp_private_buffer *pp_buffer = NULL, *first_entry = NULL;
108         tbm_surface_h src;
109         tbm_surface_h dst;
110
111         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
112
113         assert(private_pp->owner_tid == syscall(SYS_gettid));
114
115         src = pp_done->src;
116         dst = pp_done->dst;
117
118         if (tdm_debug_dump & TDM_DUMP_FLAG_PP) {
119                 /* LCOV_EXCL_START */
120                 char str[TDM_PATH_LEN];
121                 static int i;
122                 snprintf(str, TDM_PATH_LEN, "pp_dst_%03d", i++);
123                 tdm_helper_dump_buffer_str(dst, tdm_debug_dump_dir, str);
124                 /* LCOV_EXCL_STOP */
125         }
126
127         if (!LIST_IS_EMPTY(&private_pp->buffer_list)) {
128                 first_entry = container_of((&private_pp->buffer_list)->next, pp_buffer, link);
129                 if (first_entry->src != src || first_entry->dst != dst)
130                         TDM_ERR("buffer(%p,%p) is skipped", first_entry->src, first_entry->dst);
131         } else {
132                 TDM_NEVER_GET_HERE();
133         }
134
135         if ((pp_buffer = _tdm_pp_find_tbm_buffers(&private_pp->buffer_list, src, dst))) {
136                 LIST_DEL(&pp_buffer->link);
137                 LIST_DELINIT(&pp_buffer->commit_link);
138
139                 if (tdm_debug_module & TDM_DEBUG_BUFFER)
140                         TDM_INFO("pp(%p) done: src(%p) dst(%p)", private_pp, src, dst);
141
142                 if (tdm_ttrace_module & TDM_TTRACE_PP) {
143                         tbm_bo bo = tbm_surface_internal_get_bo(dst, 0);
144                         TDM_TRACE_ASYNC_END((intptr_t)private_pp, "[PP] %d", tbm_bo_export(bo));
145                 }
146
147                 _pthread_mutex_unlock(&private_display->lock);
148                 if (private_pp->done_func)
149                         private_pp->done_func(private_pp, src, dst, private_pp->done_user_data);
150                 tdm_buffer_unref_backend(src);
151                 tdm_buffer_unref_backend(dst);
152                 _pthread_mutex_lock(&private_display->lock);
153
154                 free(pp_buffer);
155         }
156 }
157
158 static void
159 _tdm_pp_cb_done(tdm_pp *pp_module, tbm_surface_h src, tbm_surface_h dst, void *user_data)
160 {
161         tdm_thread_cb_pp_done pp_done;
162         tdm_private_pp *private_pp = user_data;
163         tdm_error ret;
164
165         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
166
167         memset(&pp_done, 0, sizeof pp_done);
168         pp_done.base.type = TDM_THREAD_CB_PP_DONE;
169         pp_done.base.length = sizeof pp_done;
170         pp_done.base.object_stamp = private_pp->stamp;
171         pp_done.base.data = NULL;
172         pp_done.base.sync = 0;
173         pp_done.src = src;
174         pp_done.dst = dst;
175
176         ret = tdm_thread_cb_call(private_pp, &pp_done.base);
177         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
178 }
179
180 static void *
181 _tdm_pp_find_object(tdm_private_display *private_display, double stamp)
182 {
183         tdm_private_module *private_module = NULL;
184         tdm_private_pp *private_pp = NULL;
185
186         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), NULL);
187
188         LIST_FOR_EACH_ENTRY(private_module, &private_display->module_list, link) {
189                 LIST_FOR_EACH_ENTRY(private_pp, &private_module->pp_list, link) {
190                         if (private_pp->stamp == stamp)
191                                 return private_pp;
192                 }
193         }
194
195         return NULL;
196 }
197
198 INTERN tdm_error
199 tdm_pp_init(tdm_private_display *private_display)
200 {
201         tdm_thread_cb_set_find_func(TDM_THREAD_CB_PP_DONE, _tdm_pp_find_object);
202
203         return TDM_ERROR_NONE;
204 }
205
206 INTERN tdm_private_pp *
207 tdm_pp_create_internal(tdm_private_module *private_module, tdm_error *error)
208 {
209         tdm_private_display *private_display;
210         tdm_func_display *func_display;
211         tdm_func_pp *func_pp;
212         tdm_private_pp *private_pp = NULL;
213         tdm_pp *pp_module = NULL;
214         tdm_error ret = TDM_ERROR_NONE;
215
216         TDM_RETURN_VAL_IF_FAIL(TDM_MUTEX_IS_LOCKED(), NULL);
217         TDM_RETURN_VAL_IF_FAIL(private_module != NULL, NULL);
218
219         private_display = private_module->private_display;
220         func_display = &private_module->func_display;
221         func_pp = &private_module->func_pp;
222
223         if (!(private_module->capabilities & TDM_DISPLAY_CAPABILITY_PP)) {
224                 /* LCOV_EXCL_START */
225                 TDM_ERR("backedn(%s) no pp capability", private_module->module_data->name);
226                 if (error)
227                         *error = TDM_ERROR_NO_CAPABILITY;
228                 return NULL;
229                 /* LCOV_EXCL_STOP */
230         }
231
232         pp_module = func_display->display_create_pp(private_module->bdata, &ret);
233         if (ret != TDM_ERROR_NONE) {
234                 /* LCOV_EXCL_START */
235                 if (error)
236                         *error = ret;
237                 return NULL;
238                 /* LCOV_EXCL_STOP */
239         }
240
241         private_pp = calloc(1, sizeof(tdm_private_pp));
242         if (!private_pp) {
243                 /* LCOV_EXCL_START */
244                 TDM_ERR("failed: alloc memory");
245                 func_pp->pp_destroy(pp_module);
246                 if (error)
247                         *error = TDM_ERROR_OUT_OF_MEMORY;
248                 return NULL;
249                 /* LCOV_EXCL_STOP */
250         }
251
252         ret = tdm_thread_cb_add(private_pp, TDM_THREAD_CB_PP_DONE, NULL, _tdm_pp_thread_cb_done, NULL);
253         if (ret != TDM_ERROR_NONE) {
254                 TDM_ERR("pp tdm_thread_cb_add failed");
255                 func_pp->pp_destroy(pp_module);
256                 free(private_pp);
257                 if (error)
258                         *error = ret;
259                 return NULL;
260         }
261
262         ret = func_pp->pp_set_done_handler(pp_module, _tdm_pp_cb_done, private_pp);
263         if (ret != TDM_ERROR_NONE) {
264                 /* LCOV_EXCL_START */
265                 TDM_ERR("spp(%p) et pp_done_handler failed", private_pp);
266                 func_pp->pp_destroy(pp_module);
267                 free(private_pp);
268                 if (error)
269                         *error = ret;
270                 return NULL;
271                 /* LCOV_EXCL_STOP */
272         }
273
274         private_pp->stamp = tdm_helper_get_time();
275         while (_tdm_pp_find_object(private_display, private_pp->stamp))
276                 private_pp->stamp++;
277
278         LIST_ADD(&private_pp->link, &private_module->pp_list);
279         private_pp->private_display = private_module->private_display;
280         private_pp->private_module = private_module;
281         private_pp->pp_module = pp_module;
282         private_pp->owner_tid = syscall(SYS_gettid);
283
284         LIST_INITHEAD(&private_pp->pending_buffer_list);
285         LIST_INITHEAD(&private_pp->buffer_list);
286
287         if (error)
288                 *error = TDM_ERROR_NONE;
289
290         return private_pp;
291 }
292
293 INTERN void
294 tdm_pp_destroy_internal(tdm_private_pp *private_pp)
295 {
296         tdm_private_display *private_display;
297         tdm_private_module *private_module;
298         tdm_func_pp *func_pp;
299         tdm_pp_private_buffer *b = NULL, *bb = NULL;
300         struct list_head clone_list;
301
302         TDM_RETURN_IF_FAIL(TDM_MUTEX_IS_LOCKED());
303
304         if (!private_pp)
305                 return;
306
307         tdm_thread_cb_remove(private_pp, TDM_THREAD_CB_PP_DONE, NULL, _tdm_pp_thread_cb_done, NULL);
308
309         private_display = private_pp->private_display;
310         private_module = private_pp->private_module;
311         func_pp = &private_module->func_pp;
312
313         LIST_DEL(&private_pp->link);
314
315         func_pp->pp_destroy(private_pp->pp_module);
316
317         if (!LIST_IS_EMPTY(&private_pp->pending_buffer_list)) {
318                 TDM_WRN("pp(%p) not finished:", private_pp);
319                 _tdm_pp_print_list(&private_pp->pending_buffer_list);
320
321                 LIST_INITHEAD(&clone_list);
322                 LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->pending_buffer_list, link) {
323                         LIST_DEL(&b->link);
324                         LIST_ADDTAIL(&b->link, &clone_list);
325                 }
326
327                 _pthread_mutex_unlock(&private_display->lock);
328                 LIST_FOR_EACH_ENTRY_SAFE(b, bb, &clone_list, link) {
329                         LIST_DEL(&b->link);
330
331                         if (tdm_ttrace_module & TDM_TTRACE_PP) {
332                                 tbm_bo bo = tbm_surface_internal_get_bo(b->dst, 0);
333                                 TDM_TRACE_ASYNC_END((intptr_t)private_pp, "[PP] %d", tbm_bo_export(bo));
334                         }
335
336                         tdm_buffer_unref_backend(b->src);
337                         tdm_buffer_unref_backend(b->dst);
338                         free(b);
339                 }
340                 _pthread_mutex_lock(&private_display->lock);
341         }
342
343         if (!LIST_IS_EMPTY(&private_pp->buffer_list)) {
344                 TDM_WRN("pp(%p) not finished:", private_pp);
345                 _tdm_pp_print_list(&private_pp->buffer_list);
346
347                 LIST_INITHEAD(&clone_list);
348                 LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->buffer_list, link) {
349                         LIST_DEL(&b->link);
350                         LIST_ADDTAIL(&b->link, &clone_list);
351                 }
352
353                 _pthread_mutex_unlock(&private_display->lock);
354                 LIST_FOR_EACH_ENTRY_SAFE(b, bb, &clone_list, link) {
355                         LIST_DEL(&b->link);
356
357                         if (tdm_ttrace_module & TDM_TTRACE_PP) {
358                                 tbm_bo bo = tbm_surface_internal_get_bo(b->dst, 0);
359                                 TDM_TRACE_ASYNC_END((intptr_t)private_pp, "[PP] %d", tbm_bo_export(bo));
360                         }
361
362                         tdm_buffer_unref_backend(b->src);
363                         tdm_buffer_unref_backend(b->dst);
364                         free(b);
365                 }
366                 _pthread_mutex_lock(&private_display->lock);
367         }
368
369         private_pp->stamp = 0;
370         free(private_pp);
371 }
372
373 EXTERN void
374 tdm_pp_destroy(tdm_pp *pp)
375 {
376         tdm_private_pp *private_pp = pp;
377         tdm_private_display *private_display;
378
379         if (!private_pp)
380                 return;
381
382         private_display = private_pp->private_display;
383
384         _pthread_mutex_lock(&private_display->lock);
385         tdm_pp_destroy_internal(private_pp);
386         _pthread_mutex_unlock(&private_display->lock);
387 }
388
389 EXTERN tdm_error
390 tdm_pp_set_info(tdm_pp *pp, tdm_info_pp *info)
391 {
392         PP_FUNC_ENTRY();
393
394         TDM_RETURN_VAL_IF_FAIL(info != NULL, TDM_ERROR_INVALID_PARAMETER);
395
396         _pthread_mutex_lock(&private_display->lock);
397
398         if (!func_pp->pp_set_info) {
399                 /* LCOV_EXCL_START */
400                 _pthread_mutex_unlock(&private_display->lock);
401                 TDM_DBG("failed: not implemented!!");
402                 return TDM_ERROR_NOT_IMPLEMENTED;
403                 /* LCOV_EXCL_STOP */
404         }
405
406         TDM_INFO("pp(%p) info: src(%ux%u %u,%u %ux%u %c%c%c%c) dst(%ux%u %u,%u %ux%u %c%c%c%c) trans(%d) sync(%d) flags(%x)",
407                          private_pp, info->src_config.size.h, info->src_config.size.v,
408                          info->src_config.pos.x, info->src_config.pos.y,
409                          info->src_config.pos.w, info->src_config.pos.h,
410                          FOURCC_STR(info->src_config.format),
411                          info->dst_config.size.h, info->dst_config.size.v,
412                          info->dst_config.pos.x, info->dst_config.pos.y,
413                          info->dst_config.pos.w, info->dst_config.pos.h,
414                          FOURCC_STR(info->dst_config.format),
415                          info->transform, info->sync, info->flags);
416
417         ret = func_pp->pp_set_info(private_pp->pp_module, info);
418         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
419
420         private_pp->info = *info;
421
422         _pthread_mutex_unlock(&private_display->lock);
423
424         return ret;
425 }
426
427 EXTERN tdm_error
428 tdm_pp_set_done_handler(tdm_pp *pp, tdm_pp_done_handler func, void *user_data)
429 {
430         tdm_private_display *private_display;
431         tdm_private_pp *private_pp;
432         tdm_error ret = TDM_ERROR_NONE;
433
434         TDM_RETURN_VAL_IF_FAIL(pp != NULL, TDM_ERROR_INVALID_PARAMETER);
435
436         private_pp = (tdm_private_pp*)pp;
437         private_display = private_pp->private_display;
438
439         TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
440
441         _pthread_mutex_lock(&private_display->lock);
442
443         private_pp->done_func = func;
444         private_pp->done_user_data = user_data;
445
446         _pthread_mutex_unlock(&private_display->lock);
447
448         return ret;
449 }
450
451 EXTERN tdm_error
452 tdm_pp_attach(tdm_pp *pp, tbm_surface_h src, tbm_surface_h dst)
453 {
454         tdm_pp_private_buffer *pp_buffer;
455
456         PP_FUNC_ENTRY();
457
458         TDM_RETURN_VAL_IF_FAIL(src != NULL, TDM_ERROR_INVALID_PARAMETER);
459         TDM_RETURN_VAL_IF_FAIL(dst != NULL, TDM_ERROR_INVALID_PARAMETER);
460
461         _pthread_mutex_lock(&private_display->lock);
462
463         if (!func_pp->pp_attach) {
464                 /* LCOV_EXCL_START */
465                 _pthread_mutex_unlock(&private_display->lock);
466                 TDM_DBG("failed: not implemented!!");
467                 return TDM_ERROR_NOT_IMPLEMENTED;
468                 /* LCOV_EXCL_STOP */
469         }
470
471         if (tdm_module_check_abi(private_module, 1, 2) &&
472                 private_module->caps_pp.max_attach_count > 0) {
473                 /* LCOV_EXCL_START */
474                 int length = LIST_LENGTH(&private_pp->pending_buffer_list) +
475                                          LIST_LENGTH(&private_pp->buffer_list);
476                 if (length >= private_module->caps_pp.max_attach_count) {
477                         _pthread_mutex_unlock(&private_display->lock);
478                         TDM_DBG("failed: backend(%s) too many attached!! max_attach_count(%d)",
479                                         private_module->module_data->name, private_module->caps_pp.max_attach_count);
480                         return TDM_ERROR_BAD_REQUEST;
481                 }
482                 /* LCOV_EXCL_STOP */
483         }
484
485         if (tdm_debug_dump & TDM_DUMP_FLAG_PP) {
486                 /* LCOV_EXCL_START */
487                 char str[TDM_PATH_LEN];
488                 static int i;
489                 snprintf(str, TDM_PATH_LEN, "pp_src_%03d", i++);
490                 tdm_helper_dump_buffer_str(src, tdm_debug_dump_dir, str);
491                 /* LCOV_EXCL_STOP */
492         }
493
494         pp_buffer = calloc(1, sizeof * pp_buffer);
495         if (!pp_buffer) {
496                 /* LCOV_EXCL_START */
497                 _pthread_mutex_unlock(&private_display->lock);
498                 TDM_ERR("alloc failed");
499                 return TDM_ERROR_OUT_OF_MEMORY;
500                 /* LCOV_EXCL_STOP */
501         }
502
503         ret = func_pp->pp_attach(private_pp->pp_module, src, dst);
504         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
505
506         if (ret != TDM_ERROR_NONE) {
507                 /* LCOV_EXCL_START */
508                 free(pp_buffer);
509                 _pthread_mutex_unlock(&private_display->lock);
510                 TDM_ERR("attach failed");
511                 return ret;
512                 /* LCOV_EXCL_STOP */
513         }
514
515         LIST_ADDTAIL(&pp_buffer->link, &private_pp->pending_buffer_list);
516         pp_buffer->src = tdm_buffer_ref_backend(src);
517         pp_buffer->dst = tdm_buffer_ref_backend(dst);
518
519         if (tdm_debug_module & TDM_DEBUG_BUFFER) {
520                 TDM_INFO("pp(%p) attached:", private_pp);
521                 _tdm_pp_print_list(&private_pp->pending_buffer_list);
522         }
523
524         if (tdm_ttrace_module & TDM_TTRACE_PP) {
525                 tbm_bo bo = tbm_surface_internal_get_bo(dst, 0);
526                 TDM_TRACE_ASYNC_BEGIN((intptr_t)pp, "[PP] %d", tbm_bo_export(bo));
527         }
528
529         _pthread_mutex_unlock(&private_display->lock);
530
531         return ret;
532 }
533
534 EXTERN tdm_error
535 tdm_pp_commit(tdm_pp *pp)
536 {
537         tdm_pp_private_buffer *b = NULL, *bb = NULL;
538         struct list_head commit_buffer_list;
539
540         PP_FUNC_ENTRY();
541
542         _pthread_mutex_lock(&private_display->lock);
543
544         if (!func_pp->pp_commit) {
545                 /* LCOV_EXCL_START */
546                 _pthread_mutex_unlock(&private_display->lock);
547                 TDM_DBG("failed: not implemented!!");
548                 return TDM_ERROR_NOT_IMPLEMENTED;
549                 /* LCOV_EXCL_STOP */
550         }
551
552         LIST_INITHEAD(&commit_buffer_list);
553
554         LIST_FOR_EACH_ENTRY_SAFE(b, bb, &private_pp->pending_buffer_list, link) {
555                 LIST_DEL(&b->link);
556                 LIST_ADDTAIL(&b->link, &private_pp->buffer_list);
557                 LIST_ADDTAIL(&b->commit_link, &commit_buffer_list);
558         }
559
560         ret = func_pp->pp_commit(private_pp->pp_module);
561         TDM_WARNING_IF_FAIL(ret == TDM_ERROR_NONE);
562
563         LIST_FOR_EACH_ENTRY_SAFE(b, bb, &commit_buffer_list, commit_link) {
564                 LIST_DELINIT(&b->commit_link);
565
566                 if (!_tdm_pp_find_buffer(&private_pp->buffer_list, b))
567                         continue;
568
569                 if (ret != TDM_ERROR_NONE) {
570                         /* Not to call the user release handler when failed.
571                          * Do we have to call this function here really?
572                          * User better use set_done_handler to know when pp is done. Using
573                          * buffer_release_handler is not good.
574                          */
575                         tdm_buffer_remove_release_handler_internal(b->src);
576                         tdm_buffer_remove_release_handler_internal(b->dst);
577
578                         _pthread_mutex_unlock(&private_display->lock);
579                         tdm_buffer_unref_backend(b->src);
580                         tdm_buffer_unref_backend(b->dst);
581                         _pthread_mutex_lock(&private_display->lock);
582                         LIST_DEL(&b->link);
583
584                         free(b);
585                 }
586         }
587
588         _pthread_mutex_unlock(&private_display->lock);
589
590         return ret;
591 }