Commit | Line | Data |
---|---|---|
86eb1c67 GH |
1 | /* |
2 | * Copyright (C) 2012 The Android Open Source Project | |
3 | * | |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
5 | * you may not use this file except in compliance with the License. | |
6 | * You may obtain a copy of the License at | |
7 | * | |
8 | * http://www.apache.org/licenses/LICENSE-2.0 | |
9 | * | |
10 | * Unless required by applicable law or agreed to in writing, software | |
11 | * distributed under the License is distributed on an "AS IS" BASIS, | |
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
13 | * See the License for the specific language governing permissions and | |
14 | * limitations under the License. | |
15 | */ | |
16 | ||
17 | #include <errno.h> | |
18 | #include <fcntl.h> | |
19 | #include <pthread.h> | |
20 | #include <stdio.h> | |
21 | #include <stdlib.h> | |
22 | ||
23 | #include <sys/ioctl.h> | |
24 | #include <sys/mman.h> | |
25 | #include <sys/time.h> | |
26 | #include <sys/resource.h> | |
27 | ||
28 | #include <s3c-fb.h> | |
29 | ||
30 | #include <EGL/egl.h> | |
31 | ||
87e707ef EG |
32 | #define HWC_REMOVE_DEPRECATED_VERSIONS 1 |
33 | ||
86eb1c67 GH |
34 | #include <cutils/log.h> |
35 | #include <hardware/gralloc.h> | |
36 | #include <hardware/hardware.h> | |
37 | #include <hardware/hwcomposer.h> | |
38 | #include <hardware_legacy/uevent.h> | |
39 | #include <utils/Vector.h> | |
40 | ||
f4cc0c30 GH |
41 | #include <sync/sync.h> |
42 | ||
86eb1c67 GH |
43 | #include "ump.h" |
44 | #include "ion.h" | |
45 | #include "gralloc_priv.h" | |
cdd61b35 | 46 | #include "exynos_gscaler.h" |
86eb1c67 | 47 | |
f6f2e546 GH |
48 | struct hwc_callback_entry { |
49 | void (*callback)(void *, private_handle_t *); | |
50 | void *data; | |
86eb1c67 GH |
51 | }; |
52 | typedef android::Vector<struct hwc_callback_entry> hwc_callback_queue_t; | |
53 | ||
31991d5b | 54 | const size_t NUM_HW_WINDOWS = 5; |
86eb1c67 | 55 | const size_t NO_FB_NEEDED = NUM_HW_WINDOWS + 1; |
31991d5b | 56 | const size_t MAX_PIXELS = 2560 * 1600 * 2; |
86eb1c67 | 57 | |
87e707ef | 58 | struct exynos5_hwc_composer_device_1_t; |
86eb1c67 GH |
59 | |
60 | struct exynos5_hwc_post_data_t { | |
f6f2e546 GH |
61 | exynos5_hwc_composer_device_1_t *pdev; |
62 | int overlay_map[NUM_HW_WINDOWS]; | |
63 | hwc_layer_1_t overlays[NUM_HW_WINDOWS]; | |
64 | int num_overlays; | |
65 | size_t fb_window; | |
66 | int fence; | |
67 | pthread_mutex_t completion_lock; | |
68 | pthread_cond_t completion; | |
86eb1c67 GH |
69 | }; |
70 | ||
87e707ef | 71 | struct exynos5_hwc_composer_device_1_t { |
f6f2e546 | 72 | hwc_composer_device_1_t base; |
86eb1c67 | 73 | |
f6f2e546 GH |
74 | int fd; |
75 | exynos5_hwc_post_data_t bufs; | |
86eb1c67 | 76 | |
f6f2e546 GH |
77 | const private_module_t *gralloc_module; |
78 | hwc_procs_t *procs; | |
79 | pthread_t vsync_thread; | |
cdd61b35 | 80 | |
f6f2e546 GH |
81 | bool hdmi_hpd; |
82 | bool hdmi_mirroring; | |
83 | void *hdmi_gsc; | |
86eb1c67 GH |
84 | }; |
85 | ||
87e707ef | 86 | static void dump_layer(hwc_layer_1_t const *l) |
86eb1c67 | 87 | { |
f6f2e546 GH |
88 | ALOGV("\ttype=%d, flags=%08x, handle=%p, tr=%02x, blend=%04x, " |
89 | "{%d,%d,%d,%d}, {%d,%d,%d,%d}", | |
90 | l->compositionType, l->flags, l->handle, l->transform, | |
91 | l->blending, | |
92 | l->sourceCrop.left, | |
93 | l->sourceCrop.top, | |
94 | l->sourceCrop.right, | |
95 | l->sourceCrop.bottom, | |
96 | l->displayFrame.left, | |
97 | l->displayFrame.top, | |
98 | l->displayFrame.right, | |
99 | l->displayFrame.bottom); | |
86eb1c67 GH |
100 | } |
101 | ||
102 | static void dump_handle(private_handle_t *h) | |
103 | { | |
f6f2e546 GH |
104 | ALOGV("\t\tformat = %d, width = %u, height = %u, bpp = %u, stride = %u", |
105 | h->format, h->width, h->height, h->bpp, h->stride); | |
86eb1c67 GH |
106 | } |
107 | ||
108 | static void dump_config(s3c_fb_win_config &c) | |
109 | { | |
f6f2e546 GH |
110 | ALOGV("\tstate = %u", c.state); |
111 | if (c.state == c.S3C_FB_WIN_STATE_BUFFER) { | |
112 | ALOGV("\t\tfd = %d, offset = %u, stride = %u, " | |
113 | "x = %d, y = %d, w = %u, h = %u, " | |
114 | "format = %u", | |
115 | c.fd, c.offset, c.stride, | |
116 | c.x, c.y, c.w, c.h, | |
117 | c.format); | |
118 | } | |
119 | else if (c.state == c.S3C_FB_WIN_STATE_COLOR) { | |
120 | ALOGV("\t\tcolor = %u", c.color); | |
121 | } | |
86eb1c67 GH |
122 | } |
123 | ||
124 | inline int WIDTH(const hwc_rect &rect) { return rect.right - rect.left; } | |
125 | inline int HEIGHT(const hwc_rect &rect) { return rect.bottom - rect.top; } | |
31991d5b GH |
126 | template<typename T> inline T max(T a, T b) { return (a > b) ? a : b; } |
127 | template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; } | |
128 | ||
129 | static bool is_transformed(const hwc_layer_1_t &layer) | |
130 | { | |
f6f2e546 | 131 | return layer.transform != 0; |
31991d5b | 132 | } |
86eb1c67 | 133 | |
87e707ef | 134 | static bool is_scaled(const hwc_layer_1_t &layer) |
86eb1c67 | 135 | { |
f6f2e546 GH |
136 | return WIDTH(layer.displayFrame) != WIDTH(layer.sourceCrop) || |
137 | HEIGHT(layer.displayFrame) != HEIGHT(layer.sourceCrop); | |
86eb1c67 GH |
138 | } |
139 | ||
140 | static enum s3c_fb_pixel_format exynos5_format_to_s3c_format(int format) | |
141 | { | |
f6f2e546 GH |
142 | switch (format) { |
143 | case HAL_PIXEL_FORMAT_RGBA_8888: | |
144 | return S3C_FB_PIXEL_FORMAT_RGBA_8888; | |
145 | case HAL_PIXEL_FORMAT_RGBX_8888: | |
146 | return S3C_FB_PIXEL_FORMAT_RGBX_8888; | |
147 | case HAL_PIXEL_FORMAT_RGBA_5551: | |
148 | return S3C_FB_PIXEL_FORMAT_RGBA_5551; | |
149 | case HAL_PIXEL_FORMAT_RGBA_4444: | |
150 | return S3C_FB_PIXEL_FORMAT_RGBA_4444; | |
151 | ||
152 | default: | |
153 | return S3C_FB_PIXEL_FORMAT_MAX; | |
154 | } | |
86eb1c67 GH |
155 | } |
156 | ||
157 | static bool exynos5_format_is_supported(int format) | |
158 | { | |
f6f2e546 | 159 | return exynos5_format_to_s3c_format(format) < S3C_FB_PIXEL_FORMAT_MAX; |
86eb1c67 GH |
160 | } |
161 | ||
162 | static bool exynos5_format_is_supported_by_gscaler(int format) | |
163 | { | |
f6f2e546 GH |
164 | switch(format) { |
165 | case HAL_PIXEL_FORMAT_RGBA_8888: | |
166 | case HAL_PIXEL_FORMAT_RGBX_8888: | |
167 | case HAL_PIXEL_FORMAT_RGB_565: | |
168 | case HAL_PIXEL_FORMAT_YV12: | |
169 | return true; | |
170 | ||
171 | default: | |
172 | return false; | |
173 | } | |
86eb1c67 GH |
174 | } |
175 | ||
176 | static uint8_t exynos5_format_to_bpp(int format) | |
177 | { | |
f6f2e546 GH |
178 | switch (format) { |
179 | case HAL_PIXEL_FORMAT_RGBA_8888: | |
180 | case HAL_PIXEL_FORMAT_RGBX_8888: | |
181 | return 32; | |
182 | ||
183 | case HAL_PIXEL_FORMAT_RGBA_5551: | |
184 | case HAL_PIXEL_FORMAT_RGBA_4444: | |
185 | return 16; | |
186 | ||
187 | default: | |
188 | ALOGW("unrecognized pixel format %u", format); | |
189 | return 0; | |
190 | } | |
86eb1c67 GH |
191 | } |
192 | ||
cdd61b35 BG |
193 | static int hdmi_enable(struct exynos5_hwc_composer_device_1_t *dev) |
194 | { | |
f6f2e546 GH |
195 | if (dev->hdmi_mirroring) |
196 | return 0; | |
197 | ||
198 | exynos_gsc_img src_info; | |
199 | exynos_gsc_img dst_info; | |
200 | ||
201 | // TODO: Don't hardcode | |
202 | int src_w = 2560; | |
203 | int src_h = 1600; | |
204 | int dst_w = 1920; | |
205 | int dst_h = 1080; | |
206 | ||
207 | dev->hdmi_gsc = exynos_gsc_create_exclusive(3, GSC_OUTPUT_MODE, GSC_OUT_TV); | |
208 | if (!dev->hdmi_gsc) { | |
209 | ALOGE("%s: exynos_gsc_create_exclusive failed", __func__); | |
210 | return -ENODEV; | |
211 | } | |
212 | ||
213 | memset(&src_info, 0, sizeof(src_info)); | |
214 | memset(&dst_info, 0, sizeof(dst_info)); | |
215 | ||
216 | src_info.w = src_w; | |
217 | src_info.h = src_h; | |
218 | src_info.fw = src_w; | |
219 | src_info.fh = src_h; | |
220 | src_info.format = HAL_PIXEL_FORMAT_BGRA_8888; | |
221 | ||
222 | dst_info.w = dst_w; | |
223 | dst_info.h = dst_h; | |
224 | dst_info.fw = dst_w; | |
225 | dst_info.fh = dst_h; | |
226 | dst_info.format = HAL_PIXEL_FORMAT_YV12; | |
227 | ||
228 | int ret = exynos_gsc_config_exclusive(dev->hdmi_gsc, &src_info, &dst_info); | |
229 | if (ret < 0) { | |
230 | ALOGE("%s: exynos_gsc_config_exclusive failed %d", __func__, ret); | |
231 | exynos_gsc_destroy(dev->hdmi_gsc); | |
232 | dev->hdmi_gsc = NULL; | |
233 | return ret; | |
234 | } | |
235 | ||
236 | dev->hdmi_mirroring = true; | |
237 | return 0; | |
cdd61b35 BG |
238 | } |
239 | ||
240 | static void hdmi_disable(struct exynos5_hwc_composer_device_1_t *dev) | |
241 | { | |
f6f2e546 GH |
242 | if (!dev->hdmi_mirroring) |
243 | return; | |
244 | exynos_gsc_destroy(dev->hdmi_gsc); | |
245 | dev->hdmi_gsc = NULL; | |
246 | dev->hdmi_mirroring = false; | |
cdd61b35 BG |
247 | } |
248 | ||
249 | static int hdmi_output(struct exynos5_hwc_composer_device_1_t *dev, private_handle_t *fb) | |
250 | { | |
f6f2e546 GH |
251 | exynos_gsc_img src_info; |
252 | exynos_gsc_img dst_info; | |
cdd61b35 | 253 | |
f6f2e546 GH |
254 | memset(&src_info, 0, sizeof(src_info)); |
255 | memset(&dst_info, 0, sizeof(dst_info)); | |
cdd61b35 | 256 | |
f6f2e546 | 257 | src_info.yaddr = fb->fd; |
cdd61b35 | 258 | |
f6f2e546 GH |
259 | int ret = exynos_gsc_run_exclusive(dev->hdmi_gsc, &src_info, &dst_info); |
260 | if (ret < 0) { | |
261 | ALOGE("%s: exynos_gsc_run_exclusive failed %d", __func__, ret); | |
262 | return ret; | |
263 | } | |
cdd61b35 | 264 | |
f6f2e546 | 265 | return 0; |
cdd61b35 BG |
266 | } |
267 | ||
f6f2e546 GH |
268 | bool exynos5_supports_overlay(hwc_layer_1_t &layer, size_t i) |
269 | { | |
270 | private_handle_t *handle = private_handle_t::dynamicCast(layer.handle); | |
271 | ||
272 | if (!handle) { | |
273 | ALOGV("\tlayer %u: handle is NULL", i); | |
274 | return false; | |
275 | } | |
276 | if (!exynos5_format_is_supported(handle->format)) { | |
277 | ALOGV("\tlayer %u: pixel format %u not supported", i, | |
278 | handle->format); | |
279 | return false; | |
280 | } | |
281 | if (is_scaled(layer)) { | |
282 | ALOGV("\tlayer %u: scaling not supported", i); | |
283 | return false; | |
284 | } | |
285 | if (is_transformed(layer)) { | |
286 | ALOGV("\tlayer %u: transformations not supported", i); | |
287 | return false; | |
288 | } | |
289 | if (layer.blending != HWC_BLENDING_NONE) { | |
290 | // TODO: support this | |
291 | ALOGV("\tlayer %u: blending not supported", i); | |
292 | return false; | |
293 | } | |
294 | ||
295 | return true; | |
86eb1c67 GH |
296 | } |
297 | ||
31991d5b GH |
298 | inline bool intersect(const hwc_rect &r1, const hwc_rect &r2) |
299 | { | |
f6f2e546 GH |
300 | return !(r1.left > r2.right || |
301 | r1.right < r2.left || | |
302 | r1.top > r2.bottom || | |
303 | r1.bottom < r2.top); | |
31991d5b GH |
304 | } |
305 | ||
306 | inline hwc_rect intersection(const hwc_rect &r1, const hwc_rect &r2) | |
307 | { | |
f6f2e546 GH |
308 | hwc_rect i; |
309 | i.top = max(r1.top, r2.top); | |
310 | i.bottom = min(r1.bottom, r2.bottom); | |
311 | i.left = max(r1.left, r2.left); | |
312 | i.right = min(r1.right, r2.right); | |
313 | return i; | |
31991d5b GH |
314 | } |
315 | ||
87e707ef | 316 | static int exynos5_prepare(hwc_composer_device_1_t *dev, hwc_layer_list_1_t* list) |
86eb1c67 | 317 | { |
f6f2e546 GH |
318 | if (!list) |
319 | return 0; | |
86eb1c67 | 320 | |
f6f2e546 | 321 | ALOGV("preparing %u layers", list->numHwLayers); |
86eb1c67 | 322 | |
f6f2e546 GH |
323 | exynos5_hwc_composer_device_1_t *pdev = |
324 | (exynos5_hwc_composer_device_1_t *)dev; | |
325 | memset(pdev->bufs.overlays, 0, sizeof(pdev->bufs.overlays)); | |
86eb1c67 | 326 | |
f6f2e546 GH |
327 | bool force_fb = false; |
328 | if (pdev->hdmi_hpd) { | |
329 | hdmi_enable(pdev); | |
330 | force_fb = true; | |
331 | } else { | |
332 | hdmi_disable(pdev); | |
333 | } | |
cdd61b35 | 334 | |
87e707ef EG |
335 | for (size_t i = 0; i < NUM_HW_WINDOWS; i++) |
336 | pdev->bufs.overlay_map[i] = -1; | |
337 | ||
f6f2e546 GH |
338 | bool fb_needed = false; |
339 | size_t first_fb = 0, last_fb = 0; | |
340 | ||
341 | // find unsupported overlays | |
342 | for (size_t i = 0; i < list->numHwLayers; i++) { | |
343 | hwc_layer_1_t &layer = list->hwLayers[i]; | |
344 | ||
345 | if (layer.compositionType == HWC_BACKGROUND && !force_fb) { | |
346 | ALOGV("\tlayer %u: background supported", i); | |
347 | continue; | |
348 | } | |
349 | ||
350 | if (exynos5_supports_overlay(list->hwLayers[i], i) && !force_fb) { | |
351 | ALOGV("\tlayer %u: overlay supported", i); | |
352 | layer.compositionType = HWC_OVERLAY; | |
353 | continue; | |
354 | } | |
355 | ||
356 | if (!fb_needed) { | |
357 | first_fb = i; | |
358 | fb_needed = true; | |
359 | } | |
360 | last_fb = i; | |
361 | layer.compositionType = HWC_FRAMEBUFFER; | |
362 | } | |
363 | ||
364 | // can't composite overlays sandwiched between framebuffers | |
365 | if (fb_needed) | |
366 | for (size_t i = first_fb; i < last_fb; i++) | |
367 | list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; | |
368 | ||
369 | // Incrementally try to add our supported layers to hardware windows. | |
370 | // If adding a layer would violate a hardware constraint, force it | |
371 | // into the framebuffer and try again. (Revisiting the entire list is | |
372 | // necessary because adding a layer to the framebuffer can cause other | |
373 | // windows to retroactively violate constraints.) | |
374 | bool changed; | |
375 | do { | |
376 | android::Vector<hwc_rect> rects; | |
377 | android::Vector<hwc_rect> overlaps; | |
378 | size_t pixels_left, windows_left; | |
379 | ||
380 | if (fb_needed) { | |
381 | hwc_rect_t fb_rect; | |
382 | fb_rect.top = fb_rect.left = 0; | |
383 | fb_rect.right = pdev->gralloc_module->xres - 1; | |
384 | fb_rect.bottom = pdev->gralloc_module->yres - 1; | |
385 | pixels_left = MAX_PIXELS - pdev->gralloc_module->xres * | |
386 | pdev->gralloc_module->yres; | |
387 | windows_left = NUM_HW_WINDOWS - 1; | |
388 | rects.push_back(fb_rect); | |
389 | } | |
390 | else { | |
391 | pixels_left = MAX_PIXELS; | |
392 | windows_left = NUM_HW_WINDOWS; | |
393 | } | |
394 | changed = false; | |
395 | ||
396 | for (size_t i = 0; i < list->numHwLayers; i++) { | |
397 | hwc_layer_1_t &layer = list->hwLayers[i]; | |
398 | ||
399 | // we've already accounted for the framebuffer above | |
400 | if (layer.compositionType == HWC_FRAMEBUFFER) | |
401 | continue; | |
402 | ||
403 | // only layer 0 can be HWC_BACKGROUND, so we can | |
404 | // unconditionally allow it without extra checks | |
405 | if (layer.compositionType == HWC_BACKGROUND) { | |
406 | windows_left--; | |
407 | continue; | |
408 | } | |
409 | ||
410 | size_t pixels_needed = WIDTH(layer.displayFrame) * | |
411 | HEIGHT(layer.displayFrame); | |
412 | bool can_compose = windows_left && pixels_needed <= pixels_left; | |
413 | ||
414 | // hwc_rect_t right and bottom values are normally exclusive; | |
415 | // the intersection logic is simpler if we make them inclusive | |
416 | hwc_rect_t visible_rect = layer.displayFrame; | |
417 | visible_rect.right--; visible_rect.bottom--; | |
418 | ||
419 | // no more than 2 layers can overlap on a given pixel | |
420 | for (size_t j = 0; can_compose && j < overlaps.size(); j++) { | |
421 | if (intersect(visible_rect, overlaps.itemAt(j))) | |
422 | can_compose = false; | |
423 | } | |
424 | ||
425 | if (!can_compose) { | |
426 | layer.compositionType = HWC_FRAMEBUFFER; | |
427 | if (!fb_needed) { | |
428 | first_fb = last_fb = i; | |
429 | fb_needed = true; | |
430 | } | |
431 | else { | |
432 | first_fb = min(i, first_fb); | |
433 | last_fb = max(i, last_fb); | |
434 | } | |
435 | changed = true; | |
436 | break; | |
437 | } | |
438 | ||
439 | for (size_t j = 0; j < rects.size(); j++) { | |
440 | const hwc_rect_t &other_rect = rects.itemAt(j); | |
441 | if (intersect(visible_rect, other_rect)) | |
442 | overlaps.push_back(intersection(visible_rect, other_rect)); | |
443 | } | |
444 | rects.push_back(visible_rect); | |
445 | pixels_left -= pixels_needed; | |
446 | windows_left--; | |
447 | } | |
448 | ||
449 | if (changed) | |
450 | for (size_t i = first_fb; i < last_fb; i++) | |
451 | list->hwLayers[i].compositionType = HWC_FRAMEBUFFER; | |
452 | } while(changed); | |
453 | ||
454 | unsigned int nextWindow = 0; | |
455 | ||
456 | for (size_t i = 0; i < list->numHwLayers; i++) { | |
457 | hwc_layer_1_t &layer = list->hwLayers[i]; | |
458 | ||
459 | if (fb_needed && i == first_fb) { | |
460 | ALOGV("assigning framebuffer to window %u\n", | |
461 | nextWindow); | |
462 | nextWindow++; | |
463 | continue; | |
464 | } | |
465 | ||
466 | if (layer.compositionType != HWC_FRAMEBUFFER) { | |
467 | ALOGV("assigning layer %u to window %u", i, nextWindow); | |
468 | pdev->bufs.overlay_map[nextWindow] = i; | |
469 | nextWindow++; | |
470 | } | |
471 | } | |
472 | ||
473 | if (fb_needed) | |
474 | pdev->bufs.fb_window = first_fb; | |
475 | else | |
476 | pdev->bufs.fb_window = NO_FB_NEEDED; | |
477 | ||
478 | for (size_t i = 0; i < list->numHwLayers; i++) { | |
479 | dump_layer(&list->hwLayers[i]); | |
480 | if(list->hwLayers[i].handle) | |
481 | dump_handle(private_handle_t::dynamicCast( | |
482 | list->hwLayers[i].handle)); | |
483 | } | |
484 | ||
485 | return 0; | |
86eb1c67 GH |
486 | } |
487 | ||
488 | static void exynos5_config_handle(private_handle_t *handle, | |
f6f2e546 GH |
489 | hwc_rect_t &sourceCrop, hwc_rect_t &displayFrame, |
490 | s3c_fb_win_config &cfg) | |
491 | { | |
492 | cfg.state = cfg.S3C_FB_WIN_STATE_BUFFER; | |
493 | cfg.fd = handle->fd; | |
494 | cfg.x = displayFrame.left; | |
495 | cfg.y = displayFrame.top; | |
496 | cfg.w = WIDTH(displayFrame); | |
497 | cfg.h = HEIGHT(displayFrame); | |
498 | cfg.format = exynos5_format_to_s3c_format(handle->format); | |
499 | uint8_t bpp = exynos5_format_to_bpp(handle->format); | |
500 | cfg.offset = (sourceCrop.top * handle->stride + sourceCrop.left) * bpp / 8; | |
501 | cfg.stride = handle->stride * bpp / 8; | |
86eb1c67 GH |
502 | } |
503 | ||
87e707ef | 504 | static void exynos5_config_overlay(hwc_layer_1_t *layer, s3c_fb_win_config &cfg, |
f6f2e546 | 505 | const private_module_t *gralloc_module) |
86eb1c67 | 506 | { |
f6f2e546 GH |
507 | if (layer->compositionType == HWC_BACKGROUND) { |
508 | hwc_color_t color = layer->backgroundColor; | |
509 | cfg.state = cfg.S3C_FB_WIN_STATE_COLOR; | |
510 | cfg.color = (color.r << 16) | (color.g << 8) | color.b; | |
511 | cfg.x = 0; | |
512 | cfg.y = 0; | |
513 | cfg.w = gralloc_module->xres; | |
514 | cfg.h = gralloc_module->yres; | |
515 | return; | |
516 | } | |
517 | ||
518 | private_handle_t *handle = private_handle_t::dynamicCast(layer->handle); | |
519 | exynos5_config_handle(handle, layer->sourceCrop, layer->displayFrame, cfg); | |
86eb1c67 GH |
520 | } |
521 | ||
522 | static void exynos5_post_callback(void *data, private_handle_t *fb) | |
523 | { | |
f6f2e546 GH |
524 | exynos5_hwc_post_data_t *pdata = (exynos5_hwc_post_data_t *)data; |
525 | ||
526 | struct s3c_fb_win_config_data win_data; | |
527 | struct s3c_fb_win_config *config = win_data.config; | |
528 | memset(config, 0, sizeof(win_data.config)); | |
529 | for (size_t i = 0; i < NUM_HW_WINDOWS; i++) { | |
530 | if (i == pdata->fb_window) { | |
531 | hwc_rect_t rect = { 0, 0, fb->width, fb->height }; | |
532 | exynos5_config_handle(fb, rect, rect, config[i]); | |
533 | } else if ( pdata->overlay_map[i] != -1) { | |
534 | exynos5_config_overlay(&pdata->overlays[i], config[i], | |
535 | pdata->pdev->gralloc_module); | |
87e707ef EG |
536 | if (pdata->overlays[i].acquireFenceFd != -1) { |
537 | int err = sync_wait(pdata->overlays[i].acquireFenceFd, 100); | |
538 | if (err != 0) | |
539 | ALOGW("fence for layer %zu didn't signal in 100 ms: %s", | |
540 | i, strerror(errno)); | |
541 | close(pdata->overlays[i].acquireFenceFd); | |
542 | } | |
543 | } | |
f6f2e546 GH |
544 | dump_config(config[i]); |
545 | } | |
86eb1c67 | 546 | |
f6f2e546 GH |
547 | int ret = ioctl(pdata->pdev->fd, S3CFB_WIN_CONFIG, &win_data); |
548 | if (ret < 0) | |
549 | ALOGE("ioctl S3CFB_WIN_CONFIG failed: %d", errno); | |
86eb1c67 | 550 | |
f6f2e546 GH |
551 | if (pdata->pdev->hdmi_mirroring) |
552 | hdmi_output(pdata->pdev, fb); | |
cdd61b35 | 553 | |
87e707ef EG |
554 | pthread_mutex_lock(&pdata->completion_lock); |
555 | pdata->fence = win_data.fence; | |
556 | pthread_cond_signal(&pdata->completion); | |
557 | pthread_mutex_unlock(&pdata->completion_lock); | |
86eb1c67 GH |
558 | } |
559 | ||
87e707ef | 560 | static int exynos5_set(struct hwc_composer_device_1 *dev, hwc_display_t dpy, |
f6f2e546 | 561 | hwc_surface_t sur, hwc_layer_list_1_t* list) |
86eb1c67 | 562 | { |
f6f2e546 GH |
563 | exynos5_hwc_composer_device_1_t *pdev = |
564 | (exynos5_hwc_composer_device_1_t *)dev; | |
86eb1c67 | 565 | |
f6f2e546 GH |
566 | if (!dpy || !sur) |
567 | return 0; | |
86eb1c67 | 568 | |
f6f2e546 GH |
569 | hwc_callback_queue_t *queue = NULL; |
570 | pthread_mutex_t *lock = NULL; | |
571 | exynos5_hwc_post_data_t *data = NULL; | |
86eb1c67 | 572 | |
f6f2e546 | 573 | if (list) { |
87e707ef EG |
574 | for (size_t i = 0; i < NUM_HW_WINDOWS; i++) { |
575 | if (pdev->bufs.overlay_map[i] != -1) { | |
576 | pdev->bufs.overlays[i] = | |
577 | list->hwLayers[pdev->bufs.overlay_map[i]]; | |
578 | } | |
579 | } | |
580 | ||
f6f2e546 GH |
581 | data = (exynos5_hwc_post_data_t *) |
582 | malloc(sizeof(exynos5_hwc_post_data_t)); | |
583 | memcpy(data, &pdev->bufs, sizeof(pdev->bufs)); | |
86eb1c67 | 584 | |
87e707ef EG |
585 | data->fence = -1; |
586 | pthread_mutex_init(&data->completion_lock, NULL); | |
587 | pthread_cond_init(&data->completion, NULL); | |
588 | ||
f6f2e546 GH |
589 | if (pdev->bufs.fb_window == NO_FB_NEEDED) { |
590 | exynos5_post_callback(data, NULL); | |
591 | } else { | |
87e707ef EG |
592 | |
593 | struct hwc_callback_entry entry; | |
594 | entry.callback = exynos5_post_callback; | |
595 | entry.data = data; | |
596 | ||
597 | queue = reinterpret_cast<hwc_callback_queue_t *>( | |
598 | pdev->gralloc_module->queue); | |
599 | lock = const_cast<pthread_mutex_t *>( | |
600 | &pdev->gralloc_module->queue_lock); | |
601 | ||
602 | pthread_mutex_lock(lock); | |
603 | queue->push_front(entry); | |
604 | pthread_mutex_unlock(lock); | |
605 | ||
f6f2e546 GH |
606 | EGLBoolean success = eglSwapBuffers((EGLDisplay)dpy, |
607 | (EGLSurface)sur); | |
87e707ef EG |
608 | if (!success) { |
609 | ALOGE("HWC_EGL_ERROR"); | |
610 | if (list) { | |
611 | pthread_mutex_lock(lock); | |
612 | queue->removeAt(0); | |
613 | pthread_mutex_unlock(lock); | |
614 | free(data); | |
615 | } | |
616 | return HWC_EGL_ERROR; | |
617 | } | |
618 | } | |
f6f2e546 | 619 | } |
86eb1c67 | 620 | |
86eb1c67 | 621 | |
87e707ef EG |
622 | pthread_mutex_lock(&data->completion_lock); |
623 | while (data->fence == -1) | |
624 | pthread_cond_wait(&data->completion, &data->completion_lock); | |
625 | pthread_mutex_unlock(&data->completion_lock); | |
626 | ||
627 | for (size_t i = 0; i < NUM_HW_WINDOWS; i++) { | |
628 | if (pdev->bufs.overlay_map[i] != -1) { | |
629 | int dup_fd = dup(data->fence); | |
630 | if (dup_fd < 0) | |
631 | ALOGW("release fence dup failed: %s", strerror(errno)); | |
632 | list->hwLayers[pdev->bufs.overlay_map[i]].releaseFenceFd = dup_fd; | |
633 | } | |
634 | } | |
635 | close(data->fence); | |
636 | free(data); | |
f6f2e546 | 637 | return 0; |
86eb1c67 GH |
638 | } |
639 | ||
87e707ef | 640 | static void exynos5_registerProcs(struct hwc_composer_device_1* dev, |
f6f2e546 | 641 | hwc_procs_t const* procs) |
86eb1c67 | 642 | { |
f6f2e546 GH |
643 | struct exynos5_hwc_composer_device_1_t* pdev = |
644 | (struct exynos5_hwc_composer_device_1_t*)dev; | |
645 | pdev->procs = const_cast<hwc_procs_t *>(procs); | |
86eb1c67 GH |
646 | } |
647 | ||
87e707ef | 648 | static int exynos5_query(struct hwc_composer_device_1* dev, int what, int *value) |
86eb1c67 | 649 | { |
f6f2e546 GH |
650 | struct exynos5_hwc_composer_device_1_t *pdev = |
651 | (struct exynos5_hwc_composer_device_1_t *)dev; | |
652 | ||
653 | switch (what) { | |
654 | case HWC_BACKGROUND_LAYER_SUPPORTED: | |
655 | // we support the background layer | |
656 | value[0] = 1; | |
657 | break; | |
658 | case HWC_VSYNC_PERIOD: | |
659 | // vsync period in nanosecond | |
660 | value[0] = 1000000000.0 / pdev->gralloc_module->fps; | |
661 | break; | |
662 | default: | |
663 | // unsupported query | |
664 | return -EINVAL; | |
665 | } | |
666 | return 0; | |
86eb1c67 GH |
667 | } |
668 | ||
87e707ef | 669 | static int exynos5_eventControl(struct hwc_composer_device_1 *dev, int event, |
f6f2e546 | 670 | int enabled) |
86eb1c67 | 671 | { |
f6f2e546 GH |
672 | struct exynos5_hwc_composer_device_1_t *pdev = |
673 | (struct exynos5_hwc_composer_device_1_t *)dev; | |
674 | ||
675 | switch (event) { | |
676 | case HWC_EVENT_VSYNC: | |
677 | __u32 val = !!enabled; | |
678 | int err = ioctl(pdev->fd, S3CFB_SET_VSYNC_INT, &val); | |
679 | if (err < 0) { | |
680 | ALOGE("vsync ioctl failed"); | |
681 | return -errno; | |
682 | } | |
683 | ||
684 | return 0; | |
685 | } | |
686 | ||
687 | return -EINVAL; | |
86eb1c67 GH |
688 | } |
689 | ||
cdd61b35 | 690 | static void handle_hdmi_uevent(struct exynos5_hwc_composer_device_1_t *pdev, |
f6f2e546 | 691 | const char *buff, int len) |
cdd61b35 | 692 | { |
f6f2e546 GH |
693 | const char *s = buff; |
694 | s += strlen(s) + 1; | |
cdd61b35 | 695 | |
f6f2e546 GH |
696 | while (*s) { |
697 | if (!strncmp(s, "SWITCH_STATE=", strlen("SWITCH_STATE="))) | |
698 | pdev->hdmi_hpd = atoi(s + strlen("SWITCH_STATE=")) == 1; | |
cdd61b35 | 699 | |
f6f2e546 GH |
700 | s += strlen(s) + 1; |
701 | if (s - buff >= len) | |
702 | break; | |
703 | } | |
cdd61b35 | 704 | |
f6f2e546 | 705 | ALOGV("HDMI HPD changed to %s", pdev->hdmi_hpd ? "enabled" : "disabled"); |
cdd61b35 | 706 | |
f6f2e546 GH |
707 | if (pdev->procs && pdev->procs->invalidate) |
708 | pdev->procs->invalidate(pdev->procs); | |
cdd61b35 BG |
709 | } |
710 | ||
3464b1dd GH |
711 | static void handle_vsync_uevent(struct exynos5_hwc_composer_device_1_t *pdev, |
712 | const char *buff, int len) | |
86eb1c67 | 713 | { |
3464b1dd GH |
714 | uint64_t timestamp = 0; |
715 | const char *s = buff; | |
716 | ||
f6f2e546 GH |
717 | if (!pdev->procs || !pdev->procs->vsync) |
718 | return; | |
86eb1c67 | 719 | |
3464b1dd GH |
720 | s += strlen(s) + 1; |
721 | ||
722 | while (*s) { | |
723 | if (!strncmp(s, "VSYNC=", strlen("VSYNC="))) | |
724 | timestamp = strtoull(s + strlen("VSYNC="), NULL, 0); | |
725 | ||
726 | s += strlen(s) + 1; | |
727 | if (s - buff >= len) | |
728 | break; | |
f6f2e546 | 729 | } |
86eb1c67 | 730 | |
3464b1dd | 731 | pdev->procs->vsync(pdev->procs, 0, timestamp); |
86eb1c67 GH |
732 | } |
733 | ||
734 | static void *hwc_vsync_thread(void *data) | |
735 | { | |
f6f2e546 GH |
736 | struct exynos5_hwc_composer_device_1_t *pdev = |
737 | (struct exynos5_hwc_composer_device_1_t *)data; | |
738 | char uevent_desc[4096]; | |
739 | memset(uevent_desc, 0, sizeof(uevent_desc)); | |
740 | ||
741 | setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); | |
742 | ||
743 | uevent_init(); | |
34a653d2 | 744 | while (true) { |
3464b1dd GH |
745 | int len = uevent_next_event(uevent_desc, |
746 | sizeof(uevent_desc) - 2); | |
f6f2e546 | 747 | |
3464b1dd GH |
748 | bool vsync = !strcmp(uevent_desc, |
749 | "change@/devices/platform/exynos5-fb.1"); | |
750 | if (vsync) | |
751 | handle_vsync_uevent(pdev, uevent_desc, len); | |
752 | ||
753 | bool hdmi = !strcmp(uevent_desc, | |
754 | "change@/devices/virtual/switch/hdmi"); | |
755 | if (hdmi) | |
756 | handle_hdmi_uevent(pdev, uevent_desc, len); | |
f6f2e546 GH |
757 | } |
758 | ||
759 | return NULL; | |
86eb1c67 GH |
760 | } |
761 | ||
00359a88 CC |
762 | static int exynos5_blank(struct hwc_composer_device_1 *dev, int blank) |
763 | { | |
764 | struct exynos5_hwc_composer_device_1_t *pdev = | |
765 | (struct exynos5_hwc_composer_device_1_t *)dev; | |
766 | ||
767 | int fb_blank = blank ? FB_BLANK_POWERDOWN : FB_BLANK_UNBLANK; | |
768 | int err = ioctl(pdev->fd, FBIOBLANK, fb_blank); | |
769 | if (err < 0) { | |
770 | ALOGE("%sblank ioctl failed", blank ? "" : "un"); | |
771 | return -errno; | |
772 | } | |
773 | ||
774 | return 0; | |
775 | } | |
776 | ||
87e707ef | 777 | struct hwc_methods_1 exynos5_methods = { |
f6f2e546 | 778 | eventControl: exynos5_eventControl, |
00359a88 | 779 | blank: exynos5_blank, |
86eb1c67 GH |
780 | }; |
781 | ||
782 | static int exynos5_close(hw_device_t* device); | |
783 | ||
784 | static int exynos5_open(const struct hw_module_t *module, const char *name, | |
f6f2e546 | 785 | struct hw_device_t **device) |
86eb1c67 | 786 | { |
f6f2e546 GH |
787 | int ret; |
788 | int sw_fd; | |
789 | ||
790 | if (strcmp(name, HWC_HARDWARE_COMPOSER)) { | |
791 | return -EINVAL; | |
792 | } | |
793 | ||
794 | struct exynos5_hwc_composer_device_1_t *dev; | |
795 | dev = (struct exynos5_hwc_composer_device_1_t *)malloc(sizeof(*dev)); | |
796 | memset(dev, 0, sizeof(*dev)); | |
797 | ||
798 | if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, | |
799 | (const struct hw_module_t **)&dev->gralloc_module)) { | |
800 | ALOGE("failed to get gralloc hw module"); | |
801 | ret = -EINVAL; | |
802 | goto err_get_module; | |
803 | } | |
804 | ||
805 | dev->fd = open("/dev/graphics/fb0", O_RDWR); | |
806 | if (dev->fd < 0) { | |
807 | ALOGE("failed to open framebuffer"); | |
808 | ret = dev->fd; | |
809 | goto err_get_module; | |
810 | } | |
811 | ||
812 | sw_fd = open("/sys/class/switch/hdmi/state", O_RDONLY); | |
813 | if (sw_fd) { | |
814 | char val; | |
815 | if (read(sw_fd, &val, 1) == 1 && val == '1') | |
816 | dev->hdmi_hpd = true; | |
817 | } | |
818 | ||
819 | dev->base.common.tag = HARDWARE_DEVICE_TAG; | |
820 | dev->base.common.version = HWC_DEVICE_API_VERSION_1_0; | |
821 | dev->base.common.module = const_cast<hw_module_t *>(module); | |
822 | dev->base.common.close = exynos5_close; | |
823 | ||
824 | dev->base.prepare = exynos5_prepare; | |
825 | dev->base.set = exynos5_set; | |
826 | dev->base.registerProcs = exynos5_registerProcs; | |
827 | dev->base.query = exynos5_query; | |
828 | dev->base.methods = &exynos5_methods; | |
829 | ||
830 | dev->bufs.pdev = dev; | |
831 | ||
832 | *device = &dev->base.common; | |
833 | ||
834 | ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev); | |
835 | if (ret) { | |
836 | ALOGE("failed to start vsync thread: %s", strerror(ret)); | |
837 | ret = -ret; | |
3464b1dd | 838 | goto err_ioctl; |
f6f2e546 GH |
839 | } |
840 | ||
841 | return 0; | |
86eb1c67 GH |
842 | |
843 | err_ioctl: | |
f6f2e546 | 844 | close(dev->fd); |
86eb1c67 | 845 | err_get_module: |
f6f2e546 GH |
846 | free(dev); |
847 | return ret; | |
86eb1c67 GH |
848 | } |
849 | ||
850 | static int exynos5_close(hw_device_t *device) | |
851 | { | |
f6f2e546 GH |
852 | struct exynos5_hwc_composer_device_1_t *dev = |
853 | (struct exynos5_hwc_composer_device_1_t *)device; | |
854 | close(dev->fd); | |
855 | return 0; | |
86eb1c67 GH |
856 | } |
857 | ||
858 | static struct hw_module_methods_t exynos5_hwc_module_methods = { | |
f6f2e546 | 859 | open: exynos5_open, |
86eb1c67 GH |
860 | }; |
861 | ||
862 | hwc_module_t HAL_MODULE_INFO_SYM = { | |
f6f2e546 GH |
863 | common: { |
864 | tag: HARDWARE_MODULE_TAG, | |
865 | module_api_version: HWC_MODULE_API_VERSION_0_1, | |
866 | hal_api_version: HARDWARE_HAL_API_VERSION, | |
867 | id: HWC_HARDWARE_MODULE_ID, | |
868 | name: "Samsung exynos5 hwcomposer module", | |
869 | author: "Google", | |
870 | methods: &exynos5_hwc_module_methods, | |
871 | } | |
86eb1c67 | 872 | }; |