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