089c06737e931ce0484d9a61b0dec85a9fd66080
[platform/core/uifw/libds-tizen.git] / src / libds-tizen / backend / tdm / output.c
1 #include <assert.h>
2 #include <stdlib.h>
3
4 #include <tbm_surface.h>
5
6 #include <libds/log.h>
7 #include "libds-tizen/allocator/tbm.h"
8
9 #include "tdm.h"
10 #include "tdm_buffer_queue.h"
11
12 static const struct ds_output_interface tdm_output_iface;
13 static bool output_init_modes(struct ds_tdm_output *output);
14 static void output_destroy(struct ds_tdm_output *output);
15 static void output_init_hwc(struct ds_tdm_output *output);
16 static bool output_update_mode(struct ds_tdm_output *output);
17 static bool output_set_pending_fb(struct ds_tdm_output *output,
18         struct ds_buffer *ds_buffer);
19 static bool output_hwc_commit(struct ds_tdm_output *output);
20
21 WL_EXPORT struct ds_tdm_output *
22 ds_tdm_output_from_output(struct ds_output *ds_output)
23 {
24     if (ds_output->iface != &tdm_output_iface) {
25         ds_err("Given ds_output is not for ds_tdm_output");
26         return NULL;
27     }
28
29     return (struct ds_tdm_output *)ds_output;
30 }
31
32 WL_EXPORT struct ds_tdm_buffer_queue *
33 ds_tdm_output_get_buffer_queue(struct ds_tdm_output *output)
34 {
35     if (output->queue)
36         return output->queue;
37
38     output->queue = create_buffer_queue(output);
39     if (!output->queue) {
40         ds_err("Could not create tbm_queue with output(%p)", output);
41         return NULL;
42     }
43
44     return output->queue;
45 }
46
47 struct ds_tdm_output *
48 create_tdm_output(struct ds_tdm_backend *tdm, tdm_output *tdm_output)
49 {
50     struct ds_tdm_output *output;
51     tdm_output_conn_status conn;
52     tdm_error err;
53
54     output = calloc(1, sizeof *output);
55     if (!output)
56         return NULL;
57
58     ds_output_init(&output->base, &tdm->base, &tdm_output_iface,
59             tdm->wl_display);
60
61     output->backend = tdm;
62     output->tdm.output = tdm_output;
63
64     err = tdm_output_get_conn_status(tdm_output, &conn);
65     if (err != TDM_ERROR_NONE) {
66         ds_err("Could not get connection status of tdm output(%p): err(%d)",
67                 tdm_output, err);
68         goto err_status;
69     }
70
71     if (conn == TDM_OUTPUT_CONN_STATUS_CONNECTED) {
72         output->status = DS_TDM_OUTPUT_CONNECTED;
73
74         if (!output_init_modes(output)) {
75             ds_err("Could not initialize modes of tdm output(%p)",
76                     tdm_output);
77             goto err_status;
78         }
79     }
80     else if (conn == TDM_OUTPUT_CONN_STATUS_DISCONNECTED) {
81         output->status = DS_TDM_OUTPUT_DISCONNECTED;
82     }
83
84     if (tdm_output_get_hwc(output->tdm.output, NULL) != NULL)
85         output_init_hwc(output);
86
87     ds_inf("TDM output(%p) created: hwc(%p)", output, output->tdm.hwc);
88
89     return output;
90
91 err_status:
92     free(output);
93
94     return NULL;
95 }
96
97 static struct ds_tdm_output *
98 tdm_output_from_output(struct ds_output *ds_output)
99 {
100     assert(ds_output->iface == &tdm_output_iface);
101     return (struct ds_tdm_output *)ds_output;
102 }
103
104 static void
105 output_iface_destroy(struct ds_output *ds_output)
106 {
107     struct ds_tdm_output *output;
108
109     output = tdm_output_from_output(ds_output);
110     output_destroy(output);
111 }
112
113 void
114 destroy_tdm_buffer(struct ds_tdm_buffer *buffer)
115 {
116     if (buffer == NULL)
117         return;
118
119     if (!buffer->released)
120         wl_list_remove(&buffer->buffer_release.link);
121
122     wl_list_remove(&buffer->buffer_destroy.link);
123     wl_list_remove(&buffer->link);
124     free(buffer);
125 }
126
127 static void
128 buffer_handle_buffer_release(struct wl_listener *listener, void *data)
129 {
130     struct ds_tdm_buffer *buffer;
131
132     buffer = wl_container_of(listener, buffer, buffer_release);
133     wl_list_remove(&buffer->buffer_release.link);
134     buffer->released = true;
135 }
136
137 static void
138 buffer_handle_buffer_destroy(struct wl_listener *listener, void *data)
139 {
140     struct ds_tdm_buffer *buffer;
141
142     buffer = wl_container_of(listener, buffer, buffer_destroy);
143     destroy_tdm_buffer(buffer);
144 }
145
146 static struct ds_tdm_buffer *
147 create_tdm_buffer(struct ds_tdm_backend *backend, struct ds_buffer *ds_buffer)
148 {
149     struct ds_tdm_buffer *buffer;
150     tbm_surface_h surface;
151
152     surface = ds_tbm_buffer_get_surface(ds_buffer);
153     if (!surface) {
154         ds_err("Could not get tbm_surface_h");
155         return NULL;
156     }
157
158     buffer = calloc(1, sizeof *buffer);
159     if (!buffer)
160         return NULL;
161
162     buffer->surface = surface;
163     buffer->buffer = ds_buffer;
164     wl_list_insert(&backend->buffers, &buffer->link);
165
166     buffer->buffer_destroy.notify = buffer_handle_buffer_destroy;
167     ds_buffer_add_destroy_listener(ds_buffer, &buffer->buffer_destroy);
168
169     return buffer;
170 }
171
172 static struct ds_tdm_buffer *
173 get_or_create_tdm_buffer(struct ds_tdm_backend *backend,
174         struct ds_buffer *ds_buffer)
175 {
176     struct ds_tdm_buffer *buffer;
177
178     wl_list_for_each(buffer, &backend->buffers, link) {
179         if (buffer->buffer == ds_buffer && buffer->released) {
180             goto out;
181         }
182     }
183
184     buffer = create_tdm_buffer(backend, ds_buffer);
185
186 out:
187     buffer->released = false;
188
189     buffer->buffer_release.notify = buffer_handle_buffer_release;
190     ds_buffer_add_release_listener(ds_buffer, &buffer->buffer_release);
191
192     ds_buffer_lock(buffer->buffer);
193
194     return buffer;
195 }
196
197 static void
198 output_update_front_buffer(struct ds_tdm_output *output)
199 {
200     if (output->front_buffer)
201         ds_buffer_unlock(output->front_buffer);
202     output->front_buffer = output->back_buffer;
203     output->back_buffer = NULL;
204 }
205
206 static void
207 output_attach_back_buffer(struct ds_tdm_output *output,
208         struct ds_buffer *buffer)
209 {
210     if (output->back_buffer)
211         ds_buffer_unlock(output->back_buffer);
212     output->back_buffer = buffer;
213 }
214
215 static bool
216 output_iface_commit(struct ds_output *ds_output)
217 {
218     struct ds_tdm_output *output;
219
220     output = tdm_output_from_output(ds_output);
221
222     if (ds_output->pending.committed & DS_OUTPUT_STATE_MODE) {
223         if (!output_update_mode(output)) {
224             ds_err("Could not update TDM mode");
225             return false;
226         }
227     }
228
229     if (ds_output->pending.committed & DS_OUTPUT_STATE_BUFFER) {
230         if (!output_set_pending_fb(output, ds_output->pending.buffer))
231             ds_err("Could not update buffer");
232     }
233
234     if (!output_hwc_commit(output)) {
235         ds_err("Could not commit tdm output");
236         if (output->back_buffer)
237             ds_buffer_unlock(output->back_buffer);
238     }
239
240     return true;
241 }
242
243 static const struct ds_output_interface tdm_output_iface =
244 {
245     .destroy = output_iface_destroy,
246     .commit = output_iface_commit,
247 };
248
249 static void
250 output_destroy(struct ds_tdm_output *output)
251 {
252     struct ds_tdm_output_mode *mode, *mode_tmp;
253
254     wl_list_for_each_safe(mode, mode_tmp, &output->base.modes, base.link) {
255         wl_list_remove(&mode->base.link);
256         free(mode);
257     }
258
259     if (output->back_buffer)
260         ds_buffer_unlock(output->back_buffer);
261
262     if (output->front_buffer)
263         ds_buffer_unlock(output->front_buffer);
264
265     if (output->queue)
266         buffer_queue_destroy(output->queue);
267
268     free(output);
269 }
270
271 static bool
272 output_init_modes(struct ds_tdm_output *output)
273 {
274     struct ds_tdm_output_mode *mode;
275     const tdm_output_mode *tdm_modes, *tdm_mode;
276     tdm_error err;
277     int num_modes, i;
278
279     err = tdm_output_get_available_modes(output->tdm.output, &tdm_modes,
280             &num_modes);
281     if (err != TDM_ERROR_NONE) {
282         ds_err("Could not get available modes: output(%p)", output);
283         return false;
284     }
285
286     ds_dbg("Detected modes:");
287
288     for (i = 0; i < num_modes; i++) {
289         tdm_mode = &tdm_modes[i];
290
291         mode = calloc(1, sizeof *mode);
292         if (!mode) {
293             ds_err("Could not allocate memory");
294             continue;
295         }
296
297         mode->tdm_mode = tdm_mode;
298         mode->base.width = tdm_mode->hdisplay;
299         mode->base.height = tdm_mode->vdisplay;
300         mode->base.refresh = (int32_t)tdm_mode->vrefresh; // FIXME
301
302         if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_PREFERRED)
303             mode->base.preferred = true;
304
305         ds_dbg("  %dx%d@%d %s", mode->base.width, mode->base.height,
306                 mode->base.refresh,
307                 mode->base.preferred ? "(preferred)" : "");
308
309         if (tdm_mode->type & TDM_OUTPUT_MODE_TYPE_DEFAULT)
310             wl_list_insert(&output->base.modes, &mode->base.link);
311         else
312             wl_list_insert(output->base.modes.prev, &mode->base.link);
313     }
314
315     return true;
316 }
317
318 static void
319 output_init_hwc(struct ds_tdm_output *output)
320 {
321     tdm_error err;
322
323     output->tdm.hwc = tdm_output_get_hwc(output->tdm.output, &err);
324     if (err != TDM_ERROR_NONE || !output->tdm.hwc) {
325         ds_err("Could not get tdm_hwc: output(%p)", output);
326         return;
327     }
328 }
329
330 static bool
331 output_update_mode(struct ds_tdm_output *output)
332 {
333     const struct ds_tdm_output_mode *mode;
334     tdm_error err;
335
336     mode = (struct ds_tdm_output_mode *)output->base.pending.mode;
337
338     ds_inf("TDM output(%p) set mode %dx%d %d mHz", output,
339             mode->base.width, mode->base.height, mode->base.refresh);
340
341     err = tdm_output_set_mode(output->tdm.output, mode->tdm_mode);
342     if (err != TDM_ERROR_NONE)
343         return false;
344
345     return true;
346 }
347
348 static bool
349 output_set_pending_fb(struct ds_tdm_output *output,
350         struct ds_buffer *ds_buffer)
351 {
352     struct ds_tdm_queue_buffer *queue_buffer;
353     struct ds_tdm_buffer *buffer;
354     tbm_surface_h surface = NULL;
355     tdm_region fb_damage;
356     tdm_error err;
357
358     if (output->queue) {
359         queue_buffer = buffer_queue_find_buffer(output->queue, ds_buffer);
360         if (queue_buffer) {
361             ds_buffer_lock(ds_buffer);
362             surface = queue_buffer->surface;
363         }
364     }
365
366     if (!surface) {
367         buffer = get_or_create_tdm_buffer(output->backend, ds_buffer);
368         if (!buffer)
369             return false;
370
371         surface = buffer->surface;
372     }
373
374     memset(&fb_damage, 0, sizeof(fb_damage));
375     err = tdm_hwc_set_client_target_buffer(output->tdm.hwc,
376             surface, fb_damage);
377     if (err != TDM_ERROR_NONE) {
378         ds_err("Could not set hwc client target buffer");
379         ds_buffer_unlock(ds_buffer);
380         return false;
381     }
382
383     output_attach_back_buffer(output, ds_buffer);
384
385     return true;
386 }
387
388 static void
389 output_hwc_commit_handler(tdm_hwc *hwc, unsigned int sequence,
390         unsigned int tv_sec, unsigned int tv_usec, void *user_data)
391 {
392     struct ds_tdm_output *output = user_data;
393
394     output_update_front_buffer(output);
395
396     wl_signal_emit(&output->base.events.frame, &output->base);
397 }
398
399 static bool
400 output_hwc_commit(struct ds_tdm_output *output)
401 {
402     tdm_error err;
403     uint32_t num_changes;
404
405     err = tdm_hwc_validate(output->tdm.hwc, NULL, 0, &num_changes);
406     if (err != TDM_ERROR_NONE) {
407         ds_err("Could not hwc validate");
408         return false;
409     }
410
411     err = tdm_hwc_accept_validation(output->tdm.hwc);
412     if (err != TDM_ERROR_NONE) {
413         ds_err("Could not hwc accept validation");
414         return false;
415     }
416
417     err = tdm_hwc_commit(output->tdm.hwc, 0, output_hwc_commit_handler,
418             output);
419     if (err != TDM_ERROR_NONE) {
420         ds_err("Could not hwc commit");
421         return false;
422     }
423
424     return true;
425 }