From a99e4e5fea639e2d163071011c2b866dde1e848b Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Sat, 15 Sep 2012 03:53:03 +0200 Subject: [PATCH] init: Support DRM/KMS to display the boot logo Change-Id: Ia42ef34a65f8aeebeedb947b2d7067154720f5d2 Signed-off-by: Laurent Pinchart --- init/Android.mk | 2 +- init/logo-drm.c | 379 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ init/logo-fb.c | 163 ++++++++++++++++++++++++ init/logo.c | 163 ------------------------ 4 files changed, 543 insertions(+), 164 deletions(-) create mode 100644 init/logo-drm.c create mode 100644 init/logo-fb.c delete mode 100644 init/logo.c diff --git a/init/Android.mk b/init/Android.mk index f3287a8..485fc7f 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -10,7 +10,7 @@ LOCAL_SRC_FILES:= \ property_service.c \ util.c \ parser.c \ - logo.c \ + logo-drm.c \ keychords.c \ signal_handler.c \ init_parser.c \ diff --git a/init/logo-drm.c b/init/logo-drm.c new file mode 100644 index 0000000..22ecf08 --- /dev/null +++ b/init/logo-drm.c @@ -0,0 +1,379 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "log.h" + +#ifdef ANDROID +#include +#else +void android_memset16(void *_ptr, unsigned short val, unsigned count) +{ + unsigned short *ptr = _ptr; + count >>= 1; + while(count--) + *ptr++ = val; +} +#endif + +struct drm { + int fd; + struct { + uint32_t crtc; + uint32_t connector; + struct drm_mode_modeinfo mode; + } kms; + struct { + unsigned int handle; + void *map; + size_t size; + size_t pitch; + } bo; + struct { + unsigned int id; + } fb; +}; + +#define U642VOID(x) ((void *)(unsigned long)(x)) +#define VOID2U64(x) ((uint64_t)(unsigned long)(x)) + +static void drm_close(struct drm *drm, bool destroy) +{ + struct drm_mode_destroy_dumb dumb; + uint32_t id; + + if (drm->bo.map) + munmap(drm->bo.map, drm->bo.size); + + if (destroy) { + if (drm->fb.id) { + id = drm->fb.id; + ioctl(drm->fd, DRM_IOCTL_MODE_RMFB, &id); + } + + if (drm->bo.handle) { + memset(&dumb, 0, sizeof dumb); + dumb.handle = drm->bo.handle; + ioctl(drm->fd, DRM_IOCTL_MODE_DESTROY_DUMB, dumb); + } + } + + close(drm->fd); +} + +static int drm_get_resources(struct drm *drm) +{ + struct drm_mode_card_res counts; + struct drm_mode_card_res res; + int ret; + +retry: + memset(&res, 0, sizeof res); + ret = ioctl(drm->fd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) + return -1; + + counts = res; + memset(&res, 0, sizeof res); + + if (counts.count_crtcs) { + res.count_crtcs = counts.count_crtcs; + res.crtc_id_ptr = VOID2U64(malloc(res.count_crtcs * + sizeof(uint32_t))); + if (!res.crtc_id_ptr) { + ret = -1; + goto done; + } + } + + if (counts.count_connectors) { + res.count_connectors = counts.count_connectors; + res.connector_id_ptr = VOID2U64(malloc(res.count_connectors * + sizeof(uint32_t))); + if (!res.connector_id_ptr) { + ret = -1; + goto done; + } + } + + ret = ioctl(drm->fd, DRM_IOCTL_MODE_GETRESOURCES, &res); + if (ret < 0) + goto done; + + /* The number of available connectors and etc may have changed with a + * hotplug event in between the ioctls, in which case the field is + * silently ignored by the kernel. + */ + if (counts.count_crtcs < res.count_crtcs || + counts.count_connectors < res.count_connectors) { + free(U642VOID(res.crtc_id_ptr)); + free(U642VOID(res.connector_id_ptr)); + goto retry; + } + + /* We need at least one CRTC and one connector. */ + if (res.count_crtcs == 0 || res.count_connectors == 0) { + ret = -1; + goto done; + } + + drm->kms.crtc = *(uint32_t *)U642VOID(res.crtc_id_ptr); + drm->kms.connector = *(uint32_t *)U642VOID(res.connector_id_ptr); + + ret = 0; + +done: + free(U642VOID(res.crtc_id_ptr)); + free(U642VOID(res.connector_id_ptr)); + + return ret; +} + +static int drm_get_modes(struct drm *drm) +{ + struct drm_mode_get_connector counts; + struct drm_mode_get_connector conn; + int ret; + +retry: + memset(&conn, 0, sizeof conn); + conn.connector_id = drm->kms.connector; + + ret = ioctl(drm->fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); + if (ret < 0) + return -1; + + counts = conn; + memset(&conn, 0, sizeof conn); + conn.connector_id = drm->kms.connector; + + if (counts.count_modes) { + conn.count_modes = counts.count_modes; + conn.modes_ptr = VOID2U64(malloc(conn.count_modes * + sizeof(struct drm_mode_modeinfo))); + if (!conn.modes_ptr) { + ret = -1; + goto done; + } + } + + ret = ioctl(drm->fd, DRM_IOCTL_MODE_GETCONNECTOR, &conn); + if (ret < 0) + goto done; + + /* The number of available connectors and etc may have changed with a + * hotplug event in between the ioctls, in which case the field is + * silently ignored by the kernel. + */ + if (counts.count_modes < conn.count_modes) { + free(U642VOID(conn.modes_ptr)); + goto retry; + } + + /* We need at least one mode. */ + if (conn.count_modes == 0) { + ret = -1; + goto done; + } + + drm->kms.mode = *(struct drm_mode_modeinfo *)U642VOID(conn.modes_ptr); + + ret = 0; + +done: + free(U642VOID(conn.modes_ptr)); + + return ret; +} + +static int drm_open(struct drm *drm) +{ + struct drm_get_cap cap = { DRM_CAP_DUMB_BUFFER, 0 }; + struct drm_mode_create_dumb dumb; + struct drm_mode_map_dumb map; + struct drm_mode_fb_cmd2 fb; + void *mem; + int ret; + + drm->fd = open("/dev/dri/card0", O_RDWR); + if (drm->fd < 0) + return -1; + + /* Verify that the driver supports dumb buffers. */ + ret = ioctl(drm->fd, DRM_IOCTL_GET_CAP, &cap); + if (ret < 0) + goto error; + if (!cap.value) + goto error; + + ret = drm_get_resources(drm); + if (ret < 0) + goto error; + + ret = drm_get_modes(drm); + if (ret < 0) + goto error; + + /* Create a buffer and map it. */ + memset(&dumb, 0, sizeof dumb); + dumb.bpp = 16; + dumb.width = drm->kms.mode.hdisplay; + dumb.height = drm->kms.mode.vdisplay; + + ret = ioctl(drm->fd, DRM_IOCTL_MODE_CREATE_DUMB, &dumb); + if (ret < 0) + goto error; + + drm->bo.handle = dumb.handle; + drm->bo.size = dumb.size; + drm->bo.pitch = dumb.pitch; + + memset(&map, 0, sizeof map); + map.handle = drm->bo.handle; + + ret = ioctl(drm->fd, DRM_IOCTL_MODE_MAP_DUMB, &map); + if (ret < 0) + goto error; + + mem = mmap(0, drm->bo.size, PROT_READ | PROT_WRITE, MAP_SHARED, drm->fd, + map.offset); + if (mem == MAP_FAILED) + goto error; + + drm->bo.map = mem; + + /* Create a frame buffer. */ + memset(&fb, 0, sizeof fb); + fb.width = drm->kms.mode.hdisplay; + fb.height = drm->kms.mode.vdisplay; + fb.pixel_format = DRM_FORMAT_RGB565; + fb.handles[0] = drm->bo.handle; + fb.pitches[0] = drm->bo.pitch; + + ret = ioctl (drm->fd, DRM_IOCTL_MODE_ADDFB2, &fb); + if (ret < 0) + goto error; + + drm->fb.id = fb.fb_id; + + return 0; + +error: + drm_close(drm, true); + return -1; +} + +static void drm_update(struct drm *drm) +{ + struct drm_mode_crtc crtc; + uint32_t connector = drm->kms.connector; + int ret; + + memset(&crtc, 0, sizeof crtc); + crtc.crtc_id = drm->kms.crtc; + crtc.fb_id = drm->fb.id; + crtc.set_connectors_ptr = VOID2U64(&connector); + crtc.count_connectors = 1; + crtc.mode = drm->kms.mode; + crtc.mode_valid = 1; + + ioctl(drm->fd, DRM_IOCTL_MODE_SETCRTC, &crtc); +} + +static int vt_set_mode(int graphics) +{ + int fd, r; + fd = open("/dev/tty0", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); + close(fd); + return r; +} + +/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ + +int load_565rle_image(char *fn) +{ + struct drm drm; + struct stat s; + unsigned short *data, *bits, *ptr; + unsigned count, max; + int fd; + + if (vt_set_mode(1)) + return -1; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + ERROR("cannot open '%s'\n", fn); + goto fail_restore_text; + } + + if (fstat(fd, &s) < 0) + goto fail_close_file; + + data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + goto fail_close_file; + + if (drm_open(&drm)) + goto fail_unmap_data; + + max = drm.bo.size; + ptr = data; + count = s.st_size; + bits = drm.bo.map; + while (count > 3) { + unsigned n = ptr[0]; + if (n > max) + break; + android_memset16(bits, ptr[1], n << 1); + bits += n; + max -= n; + ptr += 2; + count -= 4; + } + + munmap(data, s.st_size); + drm_update(&drm); + drm_close(&drm, false); + close(fd); + unlink(fn); + return 0; + +fail_unmap_data: + munmap(data, s.st_size); +fail_close_file: + close(fd); +fail_restore_text: + vt_set_mode(0); + return -1; +} diff --git a/init/logo-fb.c b/init/logo-fb.c new file mode 100644 index 0000000..614224c --- /dev/null +++ b/init/logo-fb.c @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "log.h" + +#ifdef ANDROID +#include +#else +void android_memset16(void *_ptr, unsigned short val, unsigned count) +{ + unsigned short *ptr = _ptr; + count >>= 1; + while(count--) + *ptr++ = val; +} +#endif + +struct FB { + unsigned short *bits; + unsigned size; + int fd; + struct fb_fix_screeninfo fi; + struct fb_var_screeninfo vi; +}; + +#define fb_width(fb) ((fb)->vi.xres) +#define fb_height(fb) ((fb)->vi.yres) +#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) + +static int fb_open(struct FB *fb) +{ + fb->fd = open("/dev/graphics/fb0", O_RDWR); + if (fb->fd < 0) + return -1; + + if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) + goto fail; + if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) + goto fail; + + fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, + MAP_SHARED, fb->fd, 0); + if (fb->bits == MAP_FAILED) + goto fail; + + return 0; + +fail: + close(fb->fd); + return -1; +} + +static void fb_close(struct FB *fb) +{ + munmap(fb->bits, fb_size(fb)); + close(fb->fd); +} + +/* there's got to be a more portable way to do this ... */ +static void fb_update(struct FB *fb) +{ + fb->vi.yoffset = 1; + ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); + fb->vi.yoffset = 0; + ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); +} + +static int vt_set_mode(int graphics) +{ + int fd, r; + fd = open("/dev/tty0", O_RDWR | O_SYNC); + if (fd < 0) + return -1; + r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); + close(fd); + return r; +} + +/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ + +int load_565rle_image(char *fn) +{ + struct FB fb; + struct stat s; + unsigned short *data, *bits, *ptr; + unsigned count, max; + int fd; + + if (vt_set_mode(1)) + return -1; + + fd = open(fn, O_RDONLY); + if (fd < 0) { + ERROR("cannot open '%s'\n", fn); + goto fail_restore_text; + } + + if (fstat(fd, &s) < 0) { + goto fail_close_file; + } + + data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); + if (data == MAP_FAILED) + goto fail_close_file; + + if (fb_open(&fb)) + goto fail_unmap_data; + + max = fb_width(&fb) * fb_height(&fb); + ptr = data; + count = s.st_size; + bits = fb.bits; + while (count > 3) { + unsigned n = ptr[0]; + if (n > max) + break; + android_memset16(bits, ptr[1], n << 1); + bits += n; + max -= n; + ptr += 2; + count -= 4; + } + + munmap(data, s.st_size); + fb_update(&fb); + fb_close(&fb); + close(fd); + unlink(fn); + return 0; + +fail_unmap_data: + munmap(data, s.st_size); +fail_close_file: + close(fd); +fail_restore_text: + vt_set_mode(0); + return -1; +} + diff --git a/init/logo.c b/init/logo.c deleted file mode 100644 index 614224c..0000000 --- a/init/logo.c +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "log.h" - -#ifdef ANDROID -#include -#else -void android_memset16(void *_ptr, unsigned short val, unsigned count) -{ - unsigned short *ptr = _ptr; - count >>= 1; - while(count--) - *ptr++ = val; -} -#endif - -struct FB { - unsigned short *bits; - unsigned size; - int fd; - struct fb_fix_screeninfo fi; - struct fb_var_screeninfo vi; -}; - -#define fb_width(fb) ((fb)->vi.xres) -#define fb_height(fb) ((fb)->vi.yres) -#define fb_size(fb) ((fb)->vi.xres * (fb)->vi.yres * 2) - -static int fb_open(struct FB *fb) -{ - fb->fd = open("/dev/graphics/fb0", O_RDWR); - if (fb->fd < 0) - return -1; - - if (ioctl(fb->fd, FBIOGET_FSCREENINFO, &fb->fi) < 0) - goto fail; - if (ioctl(fb->fd, FBIOGET_VSCREENINFO, &fb->vi) < 0) - goto fail; - - fb->bits = mmap(0, fb_size(fb), PROT_READ | PROT_WRITE, - MAP_SHARED, fb->fd, 0); - if (fb->bits == MAP_FAILED) - goto fail; - - return 0; - -fail: - close(fb->fd); - return -1; -} - -static void fb_close(struct FB *fb) -{ - munmap(fb->bits, fb_size(fb)); - close(fb->fd); -} - -/* there's got to be a more portable way to do this ... */ -static void fb_update(struct FB *fb) -{ - fb->vi.yoffset = 1; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); - fb->vi.yoffset = 0; - ioctl(fb->fd, FBIOPUT_VSCREENINFO, &fb->vi); -} - -static int vt_set_mode(int graphics) -{ - int fd, r; - fd = open("/dev/tty0", O_RDWR | O_SYNC); - if (fd < 0) - return -1; - r = ioctl(fd, KDSETMODE, (void*) (graphics ? KD_GRAPHICS : KD_TEXT)); - close(fd); - return r; -} - -/* 565RLE image format: [count(2 bytes), rle(2 bytes)] */ - -int load_565rle_image(char *fn) -{ - struct FB fb; - struct stat s; - unsigned short *data, *bits, *ptr; - unsigned count, max; - int fd; - - if (vt_set_mode(1)) - return -1; - - fd = open(fn, O_RDONLY); - if (fd < 0) { - ERROR("cannot open '%s'\n", fn); - goto fail_restore_text; - } - - if (fstat(fd, &s) < 0) { - goto fail_close_file; - } - - data = mmap(0, s.st_size, PROT_READ, MAP_SHARED, fd, 0); - if (data == MAP_FAILED) - goto fail_close_file; - - if (fb_open(&fb)) - goto fail_unmap_data; - - max = fb_width(&fb) * fb_height(&fb); - ptr = data; - count = s.st_size; - bits = fb.bits; - while (count > 3) { - unsigned n = ptr[0]; - if (n > max) - break; - android_memset16(bits, ptr[1], n << 1); - bits += n; - max -= n; - ptr += 2; - count -= 4; - } - - munmap(data, s.st_size); - fb_update(&fb); - fb_close(&fb); - close(fd); - unlink(fn); - return 0; - -fail_unmap_data: - munmap(data, s.st_size); -fail_close_file: - close(fd); -fail_restore_text: - vt_set_mode(0); - return -1; -} - -- 1.7.8.6