hwc.cpp revision cdd61b35ae02e1fe761815ad9bd166db4098ece2
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
32#define HWC_REMOVE_DEPRECATED_VERSIONS 1
33
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
41#include <sync/sync.h>
42
43#include "ump.h"
44#include "ion.h"
45#include "gralloc_priv.h"
46#include "exynos_gscaler.h"
47
48struct hwc_callback_entry
49{
50	void (*callback)(void *, private_handle_t *);
51	void *data;
52};
53typedef android::Vector<struct hwc_callback_entry> hwc_callback_queue_t;
54
55const size_t NUM_HW_WINDOWS = 2;
56const size_t NO_FB_NEEDED = NUM_HW_WINDOWS + 1;
57
58struct exynos5_hwc_composer_device_1_t;
59
60struct exynos5_hwc_post_data_t {
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;
69};
70
71struct exynos5_hwc_composer_device_1_t {
72	hwc_composer_device_1_t	base;
73
74	int			fd;
75	exynos5_hwc_post_data_t	bufs;
76
77	const private_module_t	*gralloc_module;
78	hwc_procs_t		*procs;
79	pthread_t		vsync_thread;
80
81	bool hdmi_hpd;
82	bool hdmi_mirroring;
83	void *hdmi_gsc;
84};
85
86static void dump_layer(hwc_layer_1_t const *l)
87{
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);
100}
101
102static void dump_handle(private_handle_t *h)
103{
104	ALOGV("\t\tformat = %d, width = %u, height = %u, bpp = %u, stride = %u",
105			h->format, h->width, h->height, h->bpp, h->stride);
106}
107
108static void dump_config(s3c_fb_win_config &c)
109{
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	}
122}
123
124inline int WIDTH(const hwc_rect &rect) { return rect.right - rect.left; }
125inline int HEIGHT(const hwc_rect &rect) { return rect.bottom - rect.top; }
126
127static bool is_scaled(const hwc_layer_1_t &layer)
128{
129	return WIDTH(layer.displayFrame) != WIDTH(layer.sourceCrop) ||
130			HEIGHT(layer.displayFrame) != HEIGHT(layer.sourceCrop);
131}
132
133static enum s3c_fb_pixel_format exynos5_format_to_s3c_format(int format)
134{
135	switch (format) {
136	case HAL_PIXEL_FORMAT_RGBA_8888:
137		return S3C_FB_PIXEL_FORMAT_RGBA_8888;
138	case HAL_PIXEL_FORMAT_RGBX_8888:
139		return S3C_FB_PIXEL_FORMAT_RGBX_8888;
140	case HAL_PIXEL_FORMAT_RGBA_5551:
141		return S3C_FB_PIXEL_FORMAT_RGBA_5551;
142	case HAL_PIXEL_FORMAT_RGBA_4444:
143		return S3C_FB_PIXEL_FORMAT_RGBA_4444;
144
145	default:
146		return S3C_FB_PIXEL_FORMAT_MAX;
147	}
148}
149
150static bool exynos5_format_is_supported(int format)
151{
152	return exynos5_format_to_s3c_format(format) < S3C_FB_PIXEL_FORMAT_MAX;
153}
154
155static bool exynos5_format_is_supported_by_gscaler(int format)
156{
157	switch(format) {
158	case HAL_PIXEL_FORMAT_RGBA_8888:
159	case HAL_PIXEL_FORMAT_RGBX_8888:
160	case HAL_PIXEL_FORMAT_RGB_565:
161	case HAL_PIXEL_FORMAT_YV12:
162		return true;
163
164	default:
165		return false;
166	}
167}
168
169static uint8_t exynos5_format_to_bpp(int format)
170{
171	switch (format) {
172	case HAL_PIXEL_FORMAT_RGBA_8888:
173	case HAL_PIXEL_FORMAT_RGBX_8888:
174		return 32;
175
176	case HAL_PIXEL_FORMAT_RGBA_5551:
177	case HAL_PIXEL_FORMAT_RGBA_4444:
178		return 16;
179
180	default:
181		ALOGW("unrecognized pixel format %u", format);
182		return 0;
183	}
184}
185
186static int hdmi_enable(struct exynos5_hwc_composer_device_1_t *dev)
187{
188	if (dev->hdmi_mirroring)
189		return 0;
190
191	exynos_gsc_img src_info;
192	exynos_gsc_img dst_info;
193
194	// TODO: Don't hardcode
195	int src_w = 2560;
196	int src_h = 1600;
197	int dst_w = 1920;
198	int dst_h = 1080;
199
200	dev->hdmi_gsc = exynos_gsc_create_exclusive(3, GSC_OUTPUT_MODE, GSC_OUT_TV);
201	if (!dev->hdmi_gsc) {
202		ALOGE("%s: exynos_gsc_create_exclusive failed", __func__);
203		return -ENODEV;
204	}
205
206	memset(&src_info, 0, sizeof(src_info));
207	memset(&dst_info, 0, sizeof(dst_info));
208
209	src_info.w = src_w;
210	src_info.h = src_h;
211	src_info.fw = src_w;
212	src_info.fh = src_h;
213	src_info.format = HAL_PIXEL_FORMAT_BGRA_8888;
214
215	dst_info.w = dst_w;
216	dst_info.h = dst_h;
217	dst_info.fw = dst_w;
218	dst_info.fh = dst_h;
219	dst_info.format = HAL_PIXEL_FORMAT_RGBA_8888;
220
221	int ret = exynos_gsc_config_exclusive(dev->hdmi_gsc, &src_info, &dst_info);
222	if (ret < 0) {
223		ALOGE("%s: exynos_gsc_config_exclusive failed %d", __func__, ret);
224		exynos_gsc_destroy(dev->hdmi_gsc);
225		dev->hdmi_gsc = NULL;
226		return ret;
227	}
228
229	dev->hdmi_mirroring = true;
230	return 0;
231}
232
233static void hdmi_disable(struct exynos5_hwc_composer_device_1_t *dev)
234{
235	if (!dev->hdmi_mirroring)
236		return;
237	exynos_gsc_destroy(dev->hdmi_gsc);
238	dev->hdmi_gsc = NULL;
239	dev->hdmi_mirroring = false;
240}
241
242static int hdmi_output(struct exynos5_hwc_composer_device_1_t *dev, private_handle_t *fb)
243{
244	exynos_gsc_img src_info;
245	exynos_gsc_img dst_info;
246
247	memset(&src_info, 0, sizeof(src_info));
248	memset(&dst_info, 0, sizeof(dst_info));
249
250	src_info.yaddr = fb->fd;
251
252	int ret = exynos_gsc_run_exclusive(dev->hdmi_gsc, &src_info, &dst_info);
253	if (ret < 0) {
254		ALOGE("%s: exynos_gsc_run_exclusive failed %d", __func__, ret);
255		return ret;
256	}
257
258	return 0;
259}
260
261bool exynos5_supports_overlay(hwc_layer_1_t &layer, size_t i) {
262	private_handle_t *handle = private_handle_t::dynamicCast(layer.handle);
263
264	if (!handle) {
265		ALOGV("\tlayer %u: handle is NULL", i);
266		return false;
267	}
268	if (!exynos5_format_is_supported(handle->format)) {
269		ALOGV("\tlayer %u: pixel format %u not supported", i,
270				handle->format);
271		return false;
272	}
273	if (is_scaled(layer)) {
274		// TODO: this can be made into an overlay if a gscaler
275		// unit is available.  Also some size and pixel format
276		// limitations (see 46-3 of datasheet)
277		ALOGV("\tlayer %u: scaling and transforming not supported", i);
278		return false;
279	}
280	if (layer.blending != HWC_BLENDING_NONE) {
281		// TODO: support this
282		ALOGV("\tlayer %u: blending not supported", i);
283		return false;
284	}
285
286	return true;
287}
288
289static int exynos5_prepare(hwc_composer_device_1_t *dev, hwc_layer_list_1_t* list)
290{
291	if (!list)
292		return 0;
293
294	ALOGV("preparing %u layers", list->numHwLayers);
295
296	exynos5_hwc_composer_device_1_t *pdev =
297			(exynos5_hwc_composer_device_1_t *)dev;
298	memset(pdev->bufs.overlays, 0, sizeof(pdev->bufs.overlays));
299
300	bool force_fb = false;
301	if (pdev->hdmi_hpd) {
302		hdmi_enable(pdev);
303		force_fb = true;
304	} else {
305		hdmi_disable(pdev);
306	}
307
308    for (size_t i = 0; i < NUM_HW_WINDOWS; i++)
309        pdev->bufs.overlay_map[i] = -1;
310
311	bool fb_needed = false;
312	size_t first_fb = 0, last_fb;
313
314	// find unsupported overlays
315	for (size_t i = 0; i < list->numHwLayers; i++) {
316		hwc_layer_1_t &layer = list->hwLayers[i];
317
318		if (layer.compositionType == HWC_BACKGROUND && !force_fb) {
319			ALOGV("\tlayer %u: background supported", i);
320			continue;
321		}
322
323		if (exynos5_supports_overlay(list->hwLayers[i], i) && !force_fb) {
324			ALOGV("\tlayer %u: overlay supported", i);
325			layer.compositionType = HWC_OVERLAY;
326			continue;
327		}
328
329		if (!fb_needed) {
330			first_fb = i;
331			fb_needed = true;
332		}
333		last_fb = i;
334		layer.compositionType = HWC_FRAMEBUFFER;
335	}
336
337	// can't composite overlays sandwiched between framebuffers
338	if (fb_needed)
339		for (size_t i = first_fb; i < last_fb; i++)
340			list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
341
342	// if we need a framebuffer but can't fit it in, reserve the last
343	// window as a framebuffer
344	if ((fb_needed && first_fb >= NUM_HW_WINDOWS) ||
345			(!fb_needed && list->numHwLayers > NUM_HW_WINDOWS)) {
346		fb_needed = true;
347		first_fb = NUM_HW_WINDOWS - 1;
348		for (size_t i = first_fb; i < list->numHwLayers; i++)
349			list->hwLayers[i].compositionType = HWC_FRAMEBUFFER;
350	}
351
352	unsigned int nextWindow = 0;
353
354	// assign as many overlays as possible to windows
355	for (size_t i = 0; i < list->numHwLayers; i++) {
356		hwc_layer_1_t &layer = list->hwLayers[i];
357
358		if (nextWindow == NUM_HW_WINDOWS) {
359			ALOGV("not enough windows to assign layer %u", i);
360			layer.compositionType = HWC_FRAMEBUFFER;
361			continue;
362		}
363
364		if (fb_needed && i == first_fb) {
365			ALOGV("assigning framebuffer to window %u\n",
366					nextWindow);
367			nextWindow++;
368			continue;
369		}
370
371		if (layer.compositionType != HWC_FRAMEBUFFER) {
372			ALOGV("assigning layer %u to window %u", i, nextWindow);
373			pdev->bufs.overlay_map[nextWindow] = i;
374			nextWindow++;
375		}
376	}
377
378	if (fb_needed)
379		pdev->bufs.fb_window = first_fb;
380	else
381		pdev->bufs.fb_window = NO_FB_NEEDED;
382
383	for (size_t i = 0; i < list->numHwLayers; i++) {
384		dump_layer(&list->hwLayers[i]);
385		if(list->hwLayers[i].handle)
386			dump_handle(private_handle_t::dynamicCast(
387					list->hwLayers[i].handle));
388	}
389
390	return 0;
391}
392
393static void exynos5_config_handle(private_handle_t *handle,
394		hwc_rect_t &sourceCrop, hwc_rect_t &displayFrame,
395		s3c_fb_win_config &cfg) {
396	cfg.state = cfg.S3C_FB_WIN_STATE_BUFFER;
397	cfg.fd = handle->fd;
398	cfg.x = displayFrame.left;
399	cfg.y = displayFrame.top;
400	cfg.w = WIDTH(displayFrame);
401	cfg.h = HEIGHT(displayFrame);
402	cfg.format = exynos5_format_to_s3c_format(handle->format);
403	uint8_t bpp = exynos5_format_to_bpp(handle->format);
404	cfg.offset = (sourceCrop.top * handle->stride + sourceCrop.left) *
405			bpp / 8;
406	cfg.stride = handle->stride * bpp / 8;
407}
408
409static void exynos5_config_overlay(hwc_layer_1_t *layer, s3c_fb_win_config &cfg,
410		const private_module_t *gralloc_module)
411{
412	if (layer->compositionType == HWC_BACKGROUND) {
413		hwc_color_t color = layer->backgroundColor;
414		cfg.state = cfg.S3C_FB_WIN_STATE_COLOR;
415		cfg.color = (color.r << 16) | (color.g << 8) | color.b;
416		cfg.x = 0;
417		cfg.y = 0;
418		cfg.w = gralloc_module->xres;
419		cfg.h = gralloc_module->yres;
420		return;
421	}
422
423	private_handle_t *handle = private_handle_t::dynamicCast(layer->handle);
424	exynos5_config_handle(handle, layer->sourceCrop, layer->displayFrame,
425			cfg);
426}
427
428static void exynos5_post_callback(void *data, private_handle_t *fb)
429{
430	exynos5_hwc_post_data_t *pdata =
431			(exynos5_hwc_post_data_t *)data;
432
433	struct s3c_fb_win_config_data win_data;
434	struct s3c_fb_win_config *config = win_data.config;
435	memset(config, 0, sizeof(win_data.config));
436	for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {
437		if (i == pdata->fb_window) {
438			hwc_rect_t rect = { 0, 0, fb->width, fb->height };
439			exynos5_config_handle(fb, rect, rect, config[i]);
440		} else if ( pdata->overlay_map[i] != -1) {
441			exynos5_config_overlay(&pdata->overlays[i], config[i],
442					pdata->pdev->gralloc_module);
443            if (pdata->overlays[i].acquireFenceFd != -1) {
444                int err = sync_wait(pdata->overlays[i].acquireFenceFd, 100);
445                if (err != 0)
446                    ALOGW("fence for layer %zu didn't signal in 100 ms: %s",
447                          i, strerror(errno));
448                close(pdata->overlays[i].acquireFenceFd);
449            }
450        }
451		dump_config(config[i]);
452	}
453
454	int ret = ioctl(pdata->pdev->fd, S3CFB_WIN_CONFIG, &win_data);
455	if (ret < 0)
456		ALOGE("ioctl S3CFB_WIN_CONFIG failed: %d", errno);
457
458	if (pdata->pdev->hdmi_mirroring)
459		hdmi_output(pdata->pdev, fb);
460
461    pthread_mutex_lock(&pdata->completion_lock);
462    pdata->fence = win_data.fence;
463    pthread_cond_signal(&pdata->completion);
464    pthread_mutex_unlock(&pdata->completion_lock);
465}
466
467static int exynos5_set(struct hwc_composer_device_1 *dev, hwc_display_t dpy,
468		hwc_surface_t sur, hwc_layer_list_1_t* list)
469{
470	exynos5_hwc_composer_device_1_t *pdev =
471			(exynos5_hwc_composer_device_1_t *)dev;
472
473	if (!dpy || !sur)
474		return 0;
475
476	hwc_callback_queue_t *queue = NULL;
477	pthread_mutex_t *lock = NULL;
478	exynos5_hwc_post_data_t *data = NULL;
479
480	if (list) {
481        for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {
482            if (pdev->bufs.overlay_map[i] != -1) {
483                pdev->bufs.overlays[i] =
484                    list->hwLayers[pdev->bufs.overlay_map[i]];
485            }
486        }
487
488		data = (exynos5_hwc_post_data_t *)
489				malloc(sizeof(exynos5_hwc_post_data_t));
490		memcpy(data, &pdev->bufs, sizeof(pdev->bufs));
491
492        data->fence = -1;
493        pthread_mutex_init(&data->completion_lock, NULL);
494        pthread_cond_init(&data->completion, NULL);
495
496		if (pdev->bufs.fb_window == NO_FB_NEEDED) {
497			exynos5_post_callback(data, NULL);
498		} else {
499
500            struct hwc_callback_entry entry;
501            entry.callback = exynos5_post_callback;
502            entry.data = data;
503
504            queue = reinterpret_cast<hwc_callback_queue_t *>(
505                pdev->gralloc_module->queue);
506            lock = const_cast<pthread_mutex_t *>(
507                &pdev->gralloc_module->queue_lock);
508
509            pthread_mutex_lock(lock);
510            queue->push_front(entry);
511            pthread_mutex_unlock(lock);
512
513            EGLBoolean success = eglSwapBuffers((EGLDisplay)dpy, (EGLSurface)sur);
514            if (!success) {
515                ALOGE("HWC_EGL_ERROR");
516                if (list) {
517                    pthread_mutex_lock(lock);
518                    queue->removeAt(0);
519                    pthread_mutex_unlock(lock);
520                    free(data);
521                }
522                return HWC_EGL_ERROR;
523            }
524        }
525	}
526
527
528    pthread_mutex_lock(&data->completion_lock);
529    while (data->fence == -1)
530        pthread_cond_wait(&data->completion, &data->completion_lock);
531    pthread_mutex_unlock(&data->completion_lock);
532
533    for (size_t i = 0; i < NUM_HW_WINDOWS; i++) {
534        if (pdev->bufs.overlay_map[i] != -1) {
535            int dup_fd = dup(data->fence);
536            if (dup_fd < 0)
537                ALOGW("release fence dup failed: %s", strerror(errno));
538            list->hwLayers[pdev->bufs.overlay_map[i]].releaseFenceFd = dup_fd;
539        }
540    }
541    close(data->fence);
542    free(data);
543	return 0;
544}
545
546static void exynos5_registerProcs(struct hwc_composer_device_1* dev,
547		hwc_procs_t const* procs)
548{
549	struct exynos5_hwc_composer_device_1_t* pdev =
550			(struct exynos5_hwc_composer_device_1_t*)dev;
551	pdev->procs = const_cast<hwc_procs_t *>(procs);
552}
553
554static int exynos5_query(struct hwc_composer_device_1* dev, int what, int *value)
555{
556	struct exynos5_hwc_composer_device_1_t *pdev =
557			(struct exynos5_hwc_composer_device_1_t *)dev;
558
559	switch (what) {
560	case HWC_BACKGROUND_LAYER_SUPPORTED:
561		// we support the background layer
562		value[0] = 1;
563		break;
564	case HWC_VSYNC_PERIOD:
565		// vsync period in nanosecond
566		value[0] = 1000000000.0 / pdev->gralloc_module->fps;
567		break;
568	default:
569		// unsupported query
570		return -EINVAL;
571	}
572	return 0;
573}
574
575static int exynos5_eventControl(struct hwc_composer_device_1 *dev, int event,
576		int enabled)
577{
578	struct exynos5_hwc_composer_device_1_t *pdev =
579			(struct exynos5_hwc_composer_device_1_t *)dev;
580
581	switch (event) {
582	case HWC_EVENT_VSYNC:
583		__u32 val = !!enabled;
584		int err = ioctl(pdev->fd, S3CFB_SET_VSYNC_INT, &val);
585		if (err < 0) {
586			ALOGE("vsync ioctl failed");
587			return -errno;
588		}
589
590		return 0;
591	}
592
593	return -EINVAL;
594}
595
596static void handle_hdmi_uevent(struct exynos5_hwc_composer_device_1_t *pdev,
597		const char *buff, int len)
598{
599	const char *s = buff;
600	s += strlen(s) + 1;
601
602	while (*s) {
603		if (!strncmp(s, "SWITCH_STATE=", strlen("SWITCH_STATE=")))
604			pdev->hdmi_hpd = atoi(s + strlen("SWITCH_STATE=")) == 1;
605
606		s += strlen(s) + 1;
607		if (s - buff >= len)
608			break;
609	}
610
611	ALOGV("HDMI HPD changed to %s", pdev->hdmi_hpd ? "enabled" : "disabled");
612
613	if (pdev->procs && pdev->procs->invalidate)
614		pdev->procs->invalidate(pdev->procs);
615}
616
617static void handle_vsync_uevent(struct exynos5_hwc_composer_device_1_t *pdev,
618		const char *buff, int len)
619{
620	uint64_t timestamp = 0;
621	const char *s = buff;
622
623	if (!pdev->procs || !pdev->procs->vsync)
624		return;
625
626	s += strlen(s) + 1;
627
628	while (*s) {
629		if (!strncmp(s, "VSYNC=", strlen("VSYNC=")))
630			timestamp = strtoull(s + strlen("VSYNC="), NULL, 0);
631
632		s += strlen(s) + 1;
633		if (s - buff >= len)
634			break;
635	}
636
637	pdev->procs->vsync(pdev->procs, 0, timestamp);
638}
639
640static void *hwc_vsync_thread(void *data)
641{
642	struct exynos5_hwc_composer_device_1_t *pdev =
643			(struct exynos5_hwc_composer_device_1_t *)data;
644	char uevent_desc[4096];
645	memset(uevent_desc, 0, sizeof(uevent_desc));
646
647	setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
648
649	uevent_init();
650	while (true) {
651		int len = uevent_next_event(uevent_desc,
652				sizeof(uevent_desc) - 2);
653
654		bool vsync = !strcmp(uevent_desc,
655				"change@/devices/platform/exynos5-fb.1");
656		if (vsync)
657			handle_vsync_uevent(pdev, uevent_desc, len);
658
659		bool hdmi = !strcmp(uevent_desc,
660				"change@/devices/virtual/switch/hdmi");
661		if (hdmi)
662			handle_hdmi_uevent(pdev, uevent_desc, len);
663	}
664
665	return NULL;
666}
667
668struct hwc_methods_1 exynos5_methods = {
669	eventControl: exynos5_eventControl,
670};
671
672static int exynos5_close(hw_device_t* device);
673
674static int exynos5_open(const struct hw_module_t *module, const char *name,
675		struct hw_device_t **device)
676{
677	int ret;
678	int sw_fd;
679
680	if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
681		return -EINVAL;
682	}
683
684	struct exynos5_hwc_composer_device_1_t *dev;
685	dev = (struct exynos5_hwc_composer_device_1_t *)malloc(sizeof(*dev));
686	memset(dev, 0, sizeof(*dev));
687
688	if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID,
689			(const struct hw_module_t **)&dev->gralloc_module)) {
690		ALOGE("failed to get gralloc hw module");
691		ret = -EINVAL;
692		goto err_get_module;
693	}
694
695	dev->fd = open("/dev/graphics/fb0", O_RDWR);
696	if (dev->fd < 0) {
697		ALOGE("failed to open framebuffer");
698		ret = dev->fd;
699		goto err_get_module;
700	}
701
702	sw_fd = open("/sys/class/switch/hdmi/state", O_RDONLY);
703	if (sw_fd) {
704		char val;
705		if (read(sw_fd, &val, 1) == 1 && val == '1')
706			dev->hdmi_hpd = true;
707	}
708
709	dev->base.common.tag = HARDWARE_DEVICE_TAG;
710	dev->base.common.version = HWC_DEVICE_API_VERSION_1_0;
711	dev->base.common.module = const_cast<hw_module_t *>(module);
712	dev->base.common.close = exynos5_close;
713
714	dev->base.prepare = exynos5_prepare;
715	dev->base.set = exynos5_set;
716	dev->base.registerProcs = exynos5_registerProcs;
717	dev->base.query = exynos5_query;
718	dev->base.methods = &exynos5_methods;
719
720	dev->bufs.pdev = dev;
721
722	*device = &dev->base.common;
723
724	ret = pthread_create(&dev->vsync_thread, NULL, hwc_vsync_thread, dev);
725	if (ret) {
726		ALOGE("failed to start vsync thread: %s", strerror(ret));
727		ret = -ret;
728		goto err_ioctl;
729	}
730
731	return 0;
732
733err_ioctl:
734	close(dev->fd);
735err_get_module:
736	free(dev);
737	return ret;
738}
739
740static int exynos5_close(hw_device_t *device)
741{
742	struct exynos5_hwc_composer_device_1_t *dev =
743			(struct exynos5_hwc_composer_device_1_t *)device;
744	close(dev->fd);
745	return 0;
746}
747
748static struct hw_module_methods_t exynos5_hwc_module_methods = {
749	open: exynos5_open,
750};
751
752hwc_module_t HAL_MODULE_INFO_SYM = {
753	common: {
754		tag: HARDWARE_MODULE_TAG,
755		module_api_version: HWC_MODULE_API_VERSION_0_1,
756		hal_api_version: HARDWARE_HAL_API_VERSION,
757		id: HWC_HARDWARE_MODULE_ID,
758		name: "Samsung exynos5 hwcomposer module",
759		author: "Google",
760		methods: &exynos5_hwc_module_methods,
761	}
762};
763