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