1 /**************************************************************************
5 * Copyright 2015 Samsung Electronics co., Ltd. All Rights Reserved.
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>
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:
22 * The above copyright notice and this permission notice (including the
23 * next paragraph) shall be included in all copies or substantial portions
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.
34 **************************************************************************/
46 #include "tdm_client.h"
48 #include "tdm_macro.h"
50 #include "tdm-client-protocol.h"
54 typedef struct _tdm_private_client_vblank tdm_private_client_vblank;
56 typedef struct _tdm_private_client {
57 struct wl_display *display;
58 struct wl_registry *registry;
60 struct list_head output_list;
62 tdm_private_client_vblank *temp_vblank;
65 typedef struct _tdm_private_client_output {
66 tdm_private_client *private_client;
68 char name[TDM_NAME_LEN];
69 struct wl_tdm_output *output;
73 tdm_output_conn_status connection;
75 struct list_head vblank_list;
76 struct list_head change_handler_list;
80 struct list_head link;
81 } tdm_private_client_output;
83 struct _tdm_private_client_vblank {
84 tdm_private_client_output *private_output;
86 struct wl_tdm_vblank *vblank;
87 struct list_head wait_list;
92 unsigned int enable_fake;
96 struct list_head link;
99 typedef struct _tdm_client_output_handler_info {
100 tdm_private_client_output *private_output;
102 tdm_client_output_change_handler func;
105 struct list_head link;
106 } tdm_client_output_handler_info;
108 typedef struct _tdm_client_wait_info {
109 tdm_private_client_vblank *private_vblank;
111 tdm_client_vblank_handler func;
115 unsigned int req_sec;
116 unsigned int req_usec;
119 struct list_head link;
120 } tdm_client_wait_info;
123 _tdm_client_vblank_cb_done(void *data, struct wl_tdm_vblank *wl_tdm_vblank,
124 uint32_t req_id, uint32_t sequence, uint32_t tv_sec,
125 uint32_t tv_usec, uint32_t error)
127 tdm_private_client_vblank *private_vblank = data;
128 tdm_client_wait_info *w = NULL, *ww = NULL;
130 TDM_RETURN_IF_FAIL(private_vblank != NULL);
132 LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
133 if (w->req_id != req_id)
137 w->func(private_vblank, error, sequence, tv_sec, tv_usec, w->user_data);
148 static const struct wl_tdm_vblank_listener tdm_client_vblank_listener = {
149 _tdm_client_vblank_cb_done,
153 _tdm_client_output_destroy(tdm_private_client_output *private_output)
155 tdm_private_client_vblank *v = NULL, *vv = NULL;
156 tdm_client_output_handler_info *h = NULL, *hh = NULL;
158 LIST_DEL(&private_output->link);
160 LIST_FOR_EACH_ENTRY_SAFE(v, vv, &private_output->vblank_list, link) {
161 TDM_ERR("vblanks SHOULD be destroyed first!");
163 v->private_output = NULL;
166 LIST_FOR_EACH_ENTRY_SAFE(h, hh, &private_output->change_handler_list, link) {
171 wl_tdm_output_destroy(private_output->output);
173 free(private_output);
177 _tdm_client_output_cb_mode(void *data, struct wl_tdm_output *wl_tdm_output,
178 uint32_t width, uint32_t height, uint32_t refresh)
180 tdm_private_client_output *private_output = (tdm_private_client_output*)data;
182 TDM_RETURN_IF_FAIL(private_output != NULL);
184 private_output->width = width;
185 private_output->height = height;
186 private_output->refresh = refresh;
188 TDM_DBG("private_output(%p) wl_tbm_output@%d width(%d) height(%d) refresh(%d)",
189 private_output, wl_proxy_get_id((struct wl_proxy*)private_output->output),
190 width, height, refresh);
194 _tdm_client_output_cb_connection(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
196 tdm_private_client_output *private_output = (tdm_private_client_output*)data;
197 tdm_client_output_handler_info *h = NULL;
200 TDM_RETURN_IF_FAIL(private_output != NULL);
202 if (private_output->connection == value)
205 private_output->connection = value;
207 TDM_DBG("private_output(%p) wl_tbm_output@%d connection(%d)",
209 wl_proxy_get_id((struct wl_proxy*)private_output->output),
213 LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
215 h->func(private_output, TDM_OUTPUT_CHANGE_CONNECTION, v, h->user_data);
220 _tdm_client_output_cb_dpms(void *data, struct wl_tdm_output *wl_tdm_output, uint32_t value)
222 tdm_private_client_output *private_output = (tdm_private_client_output*)data;
223 tdm_client_output_handler_info *h = NULL;
226 TDM_RETURN_IF_FAIL(private_output != NULL);
228 if (private_output->dpms == value)
231 private_output->dpms = value;
233 TDM_DBG("private_output(%p) wl_tbm_output@%d dpms(%d)",
235 wl_proxy_get_id((struct wl_proxy*)private_output->output),
239 LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
241 h->func(private_output, TDM_OUTPUT_CHANGE_DPMS, v, h->user_data);
245 static const struct wl_tdm_output_listener tdm_client_output_listener = {
246 _tdm_client_output_cb_mode,
247 _tdm_client_output_cb_connection,
248 _tdm_client_output_cb_dpms,
252 _tdm_client_cb_global(void *data, struct wl_registry *registry,
253 uint32_t name, const char *interface,
256 tdm_private_client *private_client = data;
258 if (strcmp(interface, "wl_tdm") == 0) {
259 private_client->tdm =
260 wl_registry_bind(registry, name, &wl_tdm_interface, version);
261 TDM_RETURN_IF_FAIL(private_client->tdm != NULL);
263 wl_display_flush(private_client->display);
268 _tdm_client_cb_global_remove(void *data, struct wl_registry *registry, uint32_t name)
272 static const struct wl_registry_listener tdm_client_registry_listener = {
273 _tdm_client_cb_global,
274 _tdm_client_cb_global_remove
278 tdm_client_create(tdm_error *error)
280 tdm_private_client *private_client;
283 debug = getenv("TDM_DEBUG");
284 if (debug && (strstr(debug, "1")))
287 private_client = calloc(1, sizeof *private_client);
288 if (!private_client) {
289 TDM_ERR("alloc failed");
291 *error = TDM_ERROR_OUT_OF_MEMORY;
295 LIST_INITHEAD(&private_client->output_list);
297 private_client->display = wl_display_connect("tdm-socket");
298 TDM_GOTO_IF_FAIL(private_client->display != NULL, create_failed);
300 private_client->registry = wl_display_get_registry(private_client->display);
301 TDM_GOTO_IF_FAIL(private_client->registry != NULL, create_failed);
303 wl_registry_add_listener(private_client->registry,
304 &tdm_client_registry_listener, private_client);
305 wl_display_roundtrip(private_client->display);
307 /* check global objects */
308 TDM_GOTO_IF_FAIL(private_client->tdm != NULL, create_failed);
311 *error = TDM_ERROR_NONE;
313 return (tdm_client*)private_client;
315 tdm_client_destroy((tdm_client*)private_client);
317 *error = TDM_ERROR_OPERATION_FAILED;
322 tdm_client_destroy(tdm_client *client)
324 tdm_private_client *private_client = (tdm_private_client*)client;
325 tdm_private_client_output *o = NULL, *oo = NULL;
330 if (private_client->temp_vblank)
331 tdm_client_vblank_destroy(private_client->temp_vblank);
333 LIST_FOR_EACH_ENTRY_SAFE(o, oo, &private_client->output_list, link) {
334 _tdm_client_output_destroy(o);
337 if (private_client->tdm)
338 wl_tdm_destroy(private_client->tdm);
339 if (private_client->registry)
340 wl_registry_destroy(private_client->registry);
341 if (private_client->display)
342 wl_display_disconnect(private_client->display);
344 free(private_client);
348 tdm_client_get_fd(tdm_client *client, int *fd)
350 tdm_private_client *private_client;
352 TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
353 TDM_RETURN_VAL_IF_FAIL(fd != NULL, TDM_ERROR_INVALID_PARAMETER);
355 private_client = (tdm_private_client*)client;
357 *fd = wl_display_get_fd(private_client->display);
359 return TDM_ERROR_OPERATION_FAILED;
361 return TDM_ERROR_NONE;
365 tdm_client_handle_events(tdm_client *client)
367 tdm_private_client *private_client;
369 TDM_RETURN_VAL_IF_FAIL(client != NULL, TDM_ERROR_INVALID_PARAMETER);
371 private_client = (tdm_private_client*)client;
373 wl_display_dispatch(private_client->display);
375 return TDM_ERROR_NONE;
378 typedef struct _tdm_client_vblank_temp {
379 tdm_client_vblank_handler2 func;
381 } tdm_client_vblank_temp;
384 _tdm_client_vblank_handler_temp(tdm_client_vblank *vblank, tdm_error error, unsigned int sequence,
385 unsigned int tv_sec, unsigned int tv_usec, void *user_data)
387 tdm_client_vblank_temp *vblank_temp = user_data;
389 TDM_RETURN_IF_FAIL(vblank_temp != NULL);
391 if (vblank_temp->func)
392 vblank_temp->func(sequence, tv_sec, tv_usec, vblank_temp->user_data);
398 tdm_client_wait_vblank(tdm_client *client, char *name,
399 int sw_timer, int interval, int sync,
400 tdm_client_vblank_handler2 func, void *user_data)
402 tdm_private_client *private_client = (tdm_private_client*)client;
403 tdm_client_output *output;
404 tdm_client_vblank_temp *vblank_temp;
407 TDM_RETURN_VAL_IF_FAIL(private_client != NULL, TDM_ERROR_INVALID_PARAMETER);
408 TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
409 TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
411 if (!private_client->temp_vblank) {
412 output = tdm_client_get_output(client, name, &ret);
413 TDM_RETURN_VAL_IF_FAIL(output != NULL, ret);
415 private_client->temp_vblank = tdm_client_output_create_vblank(output, &ret);
416 TDM_RETURN_VAL_IF_FAIL(private_client->temp_vblank != NULL, ret);
419 tdm_client_vblank_set_enable_fake(private_client->temp_vblank, sw_timer);
420 tdm_client_vblank_set_sync(private_client->temp_vblank, sync);
422 vblank_temp = calloc(1, sizeof *vblank_temp);
423 TDM_RETURN_VAL_IF_FAIL(vblank_temp != NULL, TDM_ERROR_OUT_OF_MEMORY);
425 vblank_temp->func = func;
426 vblank_temp->user_data = user_data;
428 return tdm_client_vblank_wait(private_client->temp_vblank, interval, _tdm_client_vblank_handler_temp, vblank_temp);
432 tdm_client_get_output(tdm_client *client, char *name, tdm_error *error)
434 tdm_private_client *private_client;
435 tdm_private_client_output *private_output = NULL;
438 *error = TDM_ERROR_NONE;
441 TDM_ERR("'!client' failed");
443 *error = TDM_ERROR_INVALID_PARAMETER;
447 private_client = (tdm_private_client*)client;
452 LIST_FOR_EACH_ENTRY(private_output, &private_client->output_list, link) {
453 if (!strncmp(private_output->name, name, TDM_NAME_LEN))
454 return (tdm_client_output*)private_output;
457 private_output = calloc(1, sizeof *private_output);
458 if (!private_output) {
459 TDM_ERR("alloc failed");
461 *error = TDM_ERROR_OUT_OF_MEMORY;
465 private_output->private_client = private_client;
467 snprintf(private_output->name, TDM_NAME_LEN, "%s", name);
468 private_output->output = wl_tdm_create_output(private_client->tdm, private_output->name);
469 if (!private_output->output) {
470 TDM_ERR("couldn't create output resource");
471 free(private_output);
473 *error = TDM_ERROR_OUT_OF_MEMORY;
477 LIST_INITHEAD(&private_output->vblank_list);
478 LIST_INITHEAD(&private_output->change_handler_list);
479 LIST_ADDTAIL(&private_output->link, &private_client->output_list);
481 wl_tdm_output_add_listener(private_output->output,
482 &tdm_client_output_listener, private_output);
483 wl_display_roundtrip(private_client->display);
485 return (tdm_client_output*)private_output;
489 tdm_client_output_add_change_handler(tdm_client_output *output,
490 tdm_client_output_change_handler func,
493 tdm_private_client_output *private_output;
494 tdm_client_output_handler_info *h;
496 TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
497 TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
499 private_output = (tdm_private_client_output*)output;
501 h = calloc(1, sizeof *h);
502 TDM_RETURN_VAL_IF_FAIL(h != NULL, TDM_ERROR_OUT_OF_MEMORY);
504 h->private_output = private_output;
506 h->user_data = user_data;
507 LIST_ADDTAIL(&h->link, &private_output->change_handler_list);
509 return TDM_ERROR_NOT_IMPLEMENTED;
513 tdm_client_output_remove_change_handler(tdm_client_output *output,
514 tdm_client_output_change_handler func,
517 tdm_private_client_output *private_output;
518 tdm_client_output_handler_info *h = NULL;
520 TDM_RETURN_IF_FAIL(output != NULL);
521 TDM_RETURN_IF_FAIL(func != NULL);
523 private_output = (tdm_private_client_output*)output;
525 LIST_FOR_EACH_ENTRY(h, &private_output->change_handler_list, link) {
526 if (h->func != func || h->user_data != user_data)
536 tdm_client_output_get_refresh_rate(tdm_client_output *output, unsigned int *refresh)
538 tdm_private_client_output *private_output;
540 TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
541 TDM_RETURN_VAL_IF_FAIL(refresh != NULL, TDM_ERROR_INVALID_PARAMETER);
543 private_output = (tdm_private_client_output*)output;
545 *refresh = private_output->refresh;
547 return TDM_ERROR_NONE;
551 tdm_client_output_get_conn_status(tdm_client_output *output, tdm_output_conn_status *status)
553 tdm_private_client_output *private_output;
555 TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
556 TDM_RETURN_VAL_IF_FAIL(status != NULL, TDM_ERROR_INVALID_PARAMETER);
558 private_output = (tdm_private_client_output*)output;
560 *status = private_output->connection;
562 return TDM_ERROR_NONE;
566 tdm_client_output_get_dpms(tdm_client_output *output, tdm_output_dpms *dpms)
568 tdm_private_client_output *private_output;
570 TDM_RETURN_VAL_IF_FAIL(output != NULL, TDM_ERROR_INVALID_PARAMETER);
571 TDM_RETURN_VAL_IF_FAIL(dpms != NULL, TDM_ERROR_INVALID_PARAMETER);
573 private_output = (tdm_private_client_output*)output;
575 *dpms = private_output->dpms;
577 return TDM_ERROR_NONE;
581 tdm_client_output_create_vblank(tdm_client_output *output, tdm_error *error)
583 tdm_private_client_output *private_output;
584 tdm_private_client_vblank *private_vblank;
587 *error = TDM_ERROR_NONE;
590 TDM_ERR("'!output' failed");
592 *error = TDM_ERROR_INVALID_PARAMETER;
596 private_output = (tdm_private_client_output*)output;
598 private_vblank = calloc(1, sizeof *private_vblank);
599 if (!private_vblank) {
600 TDM_ERR("alloc failed");
602 *error = TDM_ERROR_OUT_OF_MEMORY;
606 private_vblank->private_output = private_output;
608 private_vblank->vblank = wl_tdm_output_create_vblank(private_output->output);
609 if (!private_vblank->vblank) {
610 TDM_ERR("couldn't create vblank resource");
611 free(private_vblank);
613 *error = TDM_ERROR_OUT_OF_MEMORY;
618 private_vblank->fps = private_output->refresh;
619 private_vblank->offset = 0;
620 private_vblank->enable_fake = 0;
622 LIST_INITHEAD(&private_vblank->wait_list);
623 LIST_ADDTAIL(&private_vblank->link, &private_output->vblank_list);
625 wl_tdm_vblank_add_listener(private_vblank->vblank,
626 &tdm_client_vblank_listener, private_vblank);
628 return (tdm_client_vblank*)private_vblank;
632 tdm_client_vblank_destroy(tdm_client_vblank *vblank)
634 tdm_private_client_vblank *private_vblank;
635 tdm_client_wait_info *w = NULL, *ww = NULL;
637 TDM_RETURN_IF_FAIL(vblank != NULL);
639 private_vblank = vblank;
640 LIST_DEL(&private_vblank->link);
642 LIST_FOR_EACH_ENTRY_SAFE(w, ww, &private_vblank->wait_list, link) {
647 wl_tdm_vblank_destroy(private_vblank->vblank);
649 free(private_vblank);
653 tdm_client_vblank_set_sync(tdm_client_vblank *vblank, unsigned int sync)
655 tdm_private_client_vblank *private_vblank;
657 TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
659 private_vblank = vblank;
660 private_vblank->sync = sync;
662 return TDM_ERROR_NONE;
666 tdm_client_vblank_set_fps(tdm_client_vblank *vblank, unsigned int fps)
668 tdm_private_client_vblank *private_vblank;
670 TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
671 TDM_RETURN_VAL_IF_FAIL(fps > 0, TDM_ERROR_INVALID_PARAMETER);
673 private_vblank = vblank;
674 TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
676 if (private_vblank->fps == fps)
677 return TDM_ERROR_NONE;
678 private_vblank->fps = fps;
680 wl_tdm_vblank_set_fps(private_vblank->vblank, fps);
682 return TDM_ERROR_NONE;
686 tdm_client_vblank_set_offset(tdm_client_vblank *vblank, int offset_ms)
688 tdm_private_client_vblank *private_vblank;
690 TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
692 private_vblank = vblank;
693 TDM_RETURN_VAL_IF_FAIL(private_vblank->started == 0, TDM_ERROR_BAD_REQUEST);
695 if (private_vblank->offset == offset_ms)
696 return TDM_ERROR_NONE;
697 private_vblank->offset = offset_ms;
699 wl_tdm_vblank_set_offset(private_vblank->vblank, offset_ms);
701 return TDM_ERROR_NONE;
705 tdm_client_vblank_set_enable_fake(tdm_client_vblank *vblank, unsigned int enable_fake)
707 tdm_private_client_vblank *private_vblank;
709 TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
711 private_vblank = vblank;
713 if (private_vblank->enable_fake == enable_fake)
714 return TDM_ERROR_NONE;
715 private_vblank->enable_fake = enable_fake;
717 wl_tdm_vblank_set_enable_fake(private_vblank->vblank, enable_fake);
719 return TDM_ERROR_NONE;
723 tdm_client_vblank_wait(tdm_client_vblank *vblank, unsigned int interval, tdm_client_vblank_handler func, void *user_data)
725 tdm_private_client *private_client;
726 tdm_private_client_output *private_output;
727 tdm_private_client_vblank *private_vblank;
728 tdm_client_wait_info *w;
732 TDM_RETURN_VAL_IF_FAIL(vblank != NULL, TDM_ERROR_INVALID_PARAMETER);
733 TDM_RETURN_VAL_IF_FAIL(func != NULL, TDM_ERROR_INVALID_PARAMETER);
734 /* can't support "interval 0" and "getting current_msc" things because
735 * there is a socket communication between TDM client and server. It's impossible
736 * to return the current msc or sequence immediately.
738 TDM_RETURN_VAL_IF_FAIL(interval > 0, TDM_ERROR_INVALID_PARAMETER);
740 private_vblank = vblank;
741 private_output = private_vblank->private_output;
742 private_client = private_output->private_client;
744 if (!private_vblank->started)
745 private_vblank->started = 1;
747 if (private_output->dpms != TDM_OUTPUT_DPMS_ON && !private_vblank->enable_fake) {
748 TDM_INFO("dpms off");
749 return TDM_ERROR_DPMS_OFF;
752 w = calloc(1, sizeof *w);
754 TDM_ERR("alloc failed");
755 return TDM_ERROR_OUT_OF_MEMORY;
758 w->private_vblank = private_vblank;
760 w->user_data = user_data;
762 LIST_ADDTAIL(&w->link, &private_vblank->wait_list);
764 clock_gettime(CLOCK_MONOTONIC, &tp);
765 w->req_id = ++private_output->req_id;
766 w->req_sec = (unsigned int)tp.tv_sec;
767 w->req_usec = (unsigned int)(tp.tv_nsec / 1000);
768 w->need_free = (private_vblank->sync) ? 0 : 1;
770 wl_tdm_vblank_wait_vblank(private_vblank->vblank, interval, w->req_id, w->req_sec, w->req_usec);
772 if (!private_vblank->sync) {
773 wl_display_flush(private_client->display);
774 return TDM_ERROR_NONE;
777 while (ret != -1 && !w->need_free)
778 ret = wl_display_dispatch(private_client->display);
780 clock_gettime(CLOCK_MONOTONIC, &tp);
781 TDM_DBG("block during %d us",
782 ((unsigned int)(tp.tv_sec * 1000000) + (unsigned int)(tp.tv_nsec / 1000))
783 - (w->req_sec * 1000000 + w->req_usec));
788 return TDM_ERROR_NONE;