diff options
Diffstat (limited to 'adf')
-rw-r--r-- | adf/Android.mk | 18 | ||||
-rw-r--r-- | adf/libadf/Android.mk | 23 | ||||
-rw-r--r-- | adf/libadf/adf.c | 810 | ||||
-rw-r--r-- | adf/libadf/include/adf/adf.h | 250 | ||||
-rw-r--r-- | adf/libadfhwc/Android.mk | 25 | ||||
-rw-r--r-- | adf/libadfhwc/adfhwc.cpp | 293 | ||||
-rw-r--r-- | adf/libadfhwc/include/adfhwc/adfhwc.h | 129 |
7 files changed, 1548 insertions, 0 deletions
diff --git a/adf/Android.mk b/adf/Android.mk new file mode 100644 index 0000000..64d486e --- /dev/null +++ b/adf/Android.mk @@ -0,0 +1,18 @@ +# +# Copyright (C) 2013 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. +# +LOCAL_PATH := $(my-dir) + +include $(call first-makefiles-under,$(LOCAL_PATH)) diff --git a/adf/libadf/Android.mk b/adf/libadf/Android.mk new file mode 100644 index 0000000..908aa6c --- /dev/null +++ b/adf/libadf/Android.mk @@ -0,0 +1,23 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := adf.c +LOCAL_MODULE := libadf +LOCAL_MODULE_TAGS := optional +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +include $(BUILD_STATIC_LIBRARY) diff --git a/adf/libadf/adf.c b/adf/libadf/adf.c new file mode 100644 index 0000000..871629e --- /dev/null +++ b/adf/libadf/adf.c @@ -0,0 +1,810 @@ +/* + * Copyright (C) 2013 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 <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include <linux/limits.h> + +#include <sys/ioctl.h> + +#include <adf/adf.h> + +#define ADF_BASE_PATH "/dev/" + +static ssize_t adf_find_nodes(const char *pattern, adf_id_t **ids) +{ + DIR *dir; + struct dirent *dirent; + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + dir = opendir(ADF_BASE_PATH); + if (!dir) + return -errno; + + errno = 0; + while ((dirent = readdir(dir))) { + adf_id_t id; + int matched = sscanf(dirent->d_name, pattern, &id); + + if (matched < 0) { + ret = -errno; + goto done; + } else if (matched != 1) { + continue; + } + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = id; + n++; + } + if (errno) + ret = -errno; + else + ret = n; + +done: + closedir(dir); + if (ret < 0) + free(ids_ret); + else + *ids = ids_ret; + return ret; +} + +ssize_t adf_devices(adf_id_t **ids) +{ + return adf_find_nodes("adf%u", ids); +} + +int adf_device_open(adf_id_t id, int flags, struct adf_device *dev) +{ + char filename[64]; + int err; + + dev->id = id; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf%u", id); + dev->fd = open(filename, flags); + if (dev->fd < 0) + return -errno; + + return 0; +} + +void adf_device_close(struct adf_device *dev) +{ + if (dev->fd >= 0) + close(dev->fd); +} + +int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + return -ENOMEM; + + if (data->n_attachments) { + data->attachments = malloc(sizeof(data->attachments[0]) * + data->n_attachments); + if (!data->attachments) + return -ENOMEM; + } + + if (data->n_allowed_attachments) { + data->allowed_attachments = + malloc(sizeof(data->allowed_attachments[0]) * + data->n_allowed_attachments); + if (!data->allowed_attachments) { + ret = -ENOMEM; + goto done; + } + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(dev->fd, ADF_GET_DEVICE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_device_data(data); + return ret; +} + +void adf_free_device_data(struct adf_device_data *data) +{ + free(data->attachments); + free(data->allowed_attachments); + free(data->custom_data); +} + +int adf_device_post(struct adf_device *dev, + adf_id_t *interfaces, size_t n_interfaces, + struct adf_buffer_config *bufs, size_t n_bufs, + void *custom_data, size_t custom_data_size) +{ + int err; + struct adf_post_config data; + + memset(&data, 0, sizeof(data)); + data.interfaces = interfaces; + data.n_interfaces = n_interfaces; + data.bufs = bufs; + data.n_bufs = n_bufs; + data.custom_data = custom_data; + data.custom_data_size = custom_data_size; + + err = ioctl(dev->fd, ADF_POST_CONFIG, &data); + if (err < 0) + return -errno; + + return (int)data.complete_fence; +} + +static int adf_device_attachment(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t interface, bool attach) +{ + int err; + struct adf_attachment_config data; + + memset(&data, 0, sizeof(data)); + data.overlay_engine = overlay_engine; + data.interface = interface; + + err = ioctl(dev->fd, attach ? ADF_ATTACH : ADF_DETACH, &data); + if (err < 0) + return -errno; + + return 0; +} + +int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, true); +} + +int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface) +{ + return adf_device_attachment(dev, overlay_engine, interface, false); +} + +ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-interface%u.%%u", dev->id); + return adf_find_nodes(pattern, interfaces); +} + +ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t **interfaces) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].overlay_engine != overlay_engine) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].interface; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *interfaces = ids_ret; + return ret; +} + +static ssize_t adf_interfaces_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_interface_data *data, __u32 match), + __u32 match) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_interface_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_interface_data data; + ret = adf_get_interface_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, match)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +static bool adf_interface_type_filter(struct adf_interface_data *data, + __u32 type) +{ + return data->type == (enum adf_interface_type)type; +} + +ssize_t adf_interfaces_filter_by_type(struct adf_device *dev, + enum adf_interface_type type, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_type_filter, + type); +} + +static bool adf_interface_flags_filter(struct adf_interface_data *data, + __u32 flag) +{ + return !!(data->flags & flag); +} + +ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag, + adf_id_t *in, size_t n_in, adf_id_t **out) +{ + return adf_interfaces_filter(dev, in, n_in, out, adf_interface_flags_filter, + flag); +} + +int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), ADF_BASE_PATH "adf-interface%u.%u", + dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_interface_data(int fd, struct adf_interface_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_available_modes) { + data->available_modes = malloc(sizeof(data->available_modes[0]) * + data->n_available_modes); + if (!data->available_modes) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_INTERFACE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_interface_data(data); + return ret; +} + +void adf_free_interface_data(struct adf_interface_data *data) +{ + free(data->available_modes); + free(data->custom_data); +} + +int adf_interface_blank(int fd, __u8 mode) +{ + int err = ioctl(fd, ADF_BLANK, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode) +{ + int err = ioctl(fd, ADF_SET_MODE, mode); + if (err < 0) + return -errno; + return 0; +} + +int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h, + __u32 format, __u32 *offset, __u32 *pitch) +{ + int err; + struct adf_simple_buffer_alloc data; + + memset(&data, 0, sizeof(data)); + data.w = w; + data.h = h; + data.format = format; + + err = ioctl(fd, ADF_SIMPLE_BUFFER_ALLOC, &data); + if (err < 0) + return -errno; + + *offset = data.offset; + *pitch = data.pitch; + return (int)data.fd; +} + +int adf_interface_simple_post(int fd, __u32 overlay_engine, + __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset, + __u32 pitch, int acquire_fence) +{ + int ret; + struct adf_simple_post_config data; + + memset(&data, 0, sizeof(data)); + data.buf.overlay_engine = overlay_engine; + data.buf.w = w; + data.buf.h = h; + data.buf.format = format; + data.buf.fd[0] = buf_fd; + data.buf.offset[0] = offset; + data.buf.pitch[0] = pitch; + data.buf.n_planes = 1; + data.buf.acquire_fence = acquire_fence; + + ret = ioctl(fd, ADF_SIMPLE_POST_CONFIG, &data); + if (ret < 0) + return -errno; + + return (int)data.complete_fence; +} + +ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines) +{ + char pattern[64]; + + snprintf(pattern, sizeof(pattern), "adf-overlay-engine%u.%%u", dev->id); + return adf_find_nodes(pattern, overlay_engines); +} + +ssize_t adf_overlay_engines_for_interface(struct adf_device *dev, + adf_id_t interface, adf_id_t **overlay_engines) +{ + struct adf_device_data data; + ssize_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + ret = adf_get_device_data(dev, &data); + if (ret < 0) + return ret; + + size_t i; + for (i = 0; i < data.n_allowed_attachments; i++) { + if (data.allowed_attachments[i].interface != interface) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = data.allowed_attachments[i].overlay_engine; + n++; + } + + ret = n; + +done: + adf_free_device_data(&data); + if (ret < 0) + free(ids_ret); + else + *overlay_engines = ids_ret; + return ret; +} + +static ssize_t adf_overlay_engines_filter(struct adf_device *dev, + adf_id_t *in, size_t n_in, adf_id_t **out, + bool (*filter)(struct adf_overlay_engine_data *data, void *cookie), + void *cookie) +{ + size_t n = 0; + ssize_t ret; + adf_id_t *ids_ret = NULL; + + size_t i; + for (i = 0; i < n_in; i++) { + int fd = adf_overlay_engine_open(dev, in[i], O_RDONLY); + if (fd < 0) { + ret = fd; + goto done; + } + + struct adf_overlay_engine_data data; + ret = adf_get_overlay_engine_data(fd, &data); + close(fd); + if (ret < 0) + goto done; + + if (!filter(&data, cookie)) + continue; + + adf_id_t *new_ids = realloc(ids_ret, (n + 1) * sizeof(ids_ret[0])); + if (!new_ids) { + ret = -ENOMEM; + goto done; + } + + ids_ret = new_ids; + ids_ret[n] = in[i]; + n++; + } + + ret = n; + +done: + if (ret < 0) + free(ids_ret); + else + *out = ids_ret; + return ret; +} + +struct format_filter_cookie { + const __u32 *formats; + size_t n_formats; +}; + +static bool adf_overlay_engine_format_filter( + struct adf_overlay_engine_data *data, void *cookie) +{ + struct format_filter_cookie *c = cookie; + size_t i; + for (i = 0; i < data->n_supported_formats; i++) { + size_t j; + for (j = 0; j < c->n_formats; j++) + if (data->supported_formats[i] == c->formats[j]) + return true; + } + return false; +} + +ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev, + const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in, + adf_id_t **out) +{ + struct format_filter_cookie cookie = { formats, n_formats }; + return adf_overlay_engines_filter(dev, in, n_in, out, + adf_overlay_engine_format_filter, &cookie); +} + +int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags) +{ + char filename[64]; + + snprintf(filename, sizeof(filename), + ADF_BASE_PATH "adf-overlay-engine%u.%u", dev->id, id); + + int fd = open(filename, flags); + if (fd < 0) + return -errno; + return fd; +} + +int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data) +{ + int err; + int ret = 0; + + memset(data, 0, sizeof(*data)); + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + return -errno; + + if (data->n_supported_formats) { + data->supported_formats = malloc(sizeof(data->supported_formats[0]) * + data->n_supported_formats); + if (!data->supported_formats) + return -ENOMEM; + } + + if (data->custom_data_size) { + data->custom_data = malloc(data->custom_data_size); + if (!data->custom_data) { + ret = -ENOMEM; + goto done; + } + } + + err = ioctl(fd, ADF_GET_OVERLAY_ENGINE_DATA, data); + if (err < 0) + ret = -errno; + +done: + if (ret < 0) + adf_free_overlay_engine_data(data); + return ret; +} + +void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data) +{ + free(data->supported_formats); + free(data->custom_data); +} + +bool adf_overlay_engine_supports_format(int fd, __u32 format) +{ + struct adf_overlay_engine_data data; + bool ret = false; + size_t i; + + int err = adf_get_overlay_engine_data(fd, &data); + if (err < 0) + return false; + + for (i = 0; i < data.n_supported_formats; i++) { + if (data.supported_formats[i] == format) { + ret = true; + break; + } + } + + adf_free_overlay_engine_data(&data); + return ret; +} + +int adf_set_event(int fd, enum adf_event_type type, bool enabled) +{ + struct adf_set_event data; + + data.type = type; + data.enabled = enabled; + + int err = ioctl(fd, ADF_SET_EVENT, &data); + if (err < 0) + return -errno; + return 0; +} + +int adf_read_event(int fd, struct adf_event **event) +{ + struct adf_event header; + struct { + struct adf_event base; + uint8_t data[0]; + } *event_ret; + size_t data_size; + int ret = 0; + + int err = read(fd, &header, sizeof(header)); + if (err < 0) + return -errno; + if ((size_t)err < sizeof(header)) + return -EIO; + if (header.length < sizeof(header)) + return -EIO; + + event_ret = malloc(header.length); + if (!event_ret) + return -ENOMEM; + data_size = header.length - sizeof(header); + + memcpy(event_ret, &header, sizeof(header)); + ssize_t read_size = read(fd, &event_ret->data, data_size); + if (read_size < 0) { + ret = -errno; + goto done; + } + if ((size_t)read_size < data_size) { + ret = -EIO; + goto done; + } + + *event = &event_ret->base; + +done: + if (ret < 0) + free(event_ret); + return ret; +} + +void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]) +{ + buf[0] = format & 0xFF; + buf[1] = (format >> 8) & 0xFF; + buf[2] = (format >> 16) & 0xFF; + buf[3] = (format >> 24) & 0xFF; + buf[4] = '\0'; +} + +static bool adf_find_simple_post_overlay_engine(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t interface, adf_id_t *overlay_engine) +{ + adf_id_t *engs; + ssize_t n_engs = adf_overlay_engines_for_interface(dev, interface, &engs); + + if (n_engs <= 0) + return false; + + adf_id_t *filtered_engs; + ssize_t n_filtered_engs = adf_overlay_engines_filter_by_format(dev, + formats, n_formats, engs, n_engs, &filtered_engs); + free(engs); + + if (n_filtered_engs <= 0) + return false; + + *overlay_engine = filtered_engs[0]; + free(filtered_engs); + return true; +} + +static const __u32 any_rgb_format[] = { + DRM_FORMAT_C8, + DRM_FORMAT_RGB332, + DRM_FORMAT_BGR233, + DRM_FORMAT_XRGB1555, + DRM_FORMAT_XBGR1555, + DRM_FORMAT_RGBX5551, + DRM_FORMAT_BGRX5551, + DRM_FORMAT_ARGB1555, + DRM_FORMAT_ABGR1555, + DRM_FORMAT_RGBA5551, + DRM_FORMAT_BGRA5551, + DRM_FORMAT_RGB565, + DRM_FORMAT_BGR565, + DRM_FORMAT_RGB888, + DRM_FORMAT_BGR888, + DRM_FORMAT_XRGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_RGBX8888, + DRM_FORMAT_BGRX8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_RGBX1010102, + DRM_FORMAT_BGRX1010102, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_RGBA1010102, + DRM_FORMAT_BGRA1010102, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_RGBA8888, + DRM_FORMAT_BGRA8888, +}; + +int adf_find_simple_post_configuration(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t *interface, adf_id_t *overlay_engine) +{ + adf_id_t *intfs; + ssize_t n_intfs = adf_interfaces(dev, &intfs); + + if (n_intfs < 0) + return n_intfs; + else if (!n_intfs) + return -ENODEV; + + adf_id_t *primary_intfs; + ssize_t n_primary_intfs = adf_interfaces_filter_by_flag(dev, + ADF_INTF_FLAG_PRIMARY, intfs, n_intfs, &primary_intfs); + free(intfs); + + if (n_primary_intfs < 0) + return n_primary_intfs; + else if (!n_primary_intfs) + return -ENODEV; + + if (!formats) { + formats = any_rgb_format; + n_formats = sizeof(any_rgb_format) / sizeof(any_rgb_format[0]); + } + + bool found = false; + ssize_t i = 0; + for (i = 0; i < n_primary_intfs; i++) { + found = adf_find_simple_post_overlay_engine(dev, formats, n_formats, + primary_intfs[i], overlay_engine); + if (found) { + *interface = primary_intfs[i]; + break; + } + } + free(primary_intfs); + + if (!found) + return -ENODEV; + + return 0; +} diff --git a/adf/libadf/include/adf/adf.h b/adf/libadf/include/adf/adf.h new file mode 100644 index 0000000..b6bda34 --- /dev/null +++ b/adf/libadf/include/adf/adf.h @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBADF_ADF_H_ +#define _LIBADF_ADF_H_ + +#include <stdint.h> +#include <stdbool.h> +#include <sys/cdefs.h> +#include <sys/types.h> +#include <video/adf.h> + +typedef __u32 adf_id_t; + +struct adf_device { + adf_id_t id; + int fd; +}; + +__BEGIN_DECLS + +/** + * Enumerates all ADF devices. + * + * Returns the number of ADF devices, and sets ids to a list of device IDs. + * The caller must free() the returned list of device IDs. + * + * On error, returns -errno. + */ +ssize_t adf_devices(adf_id_t **ids); + +/** + * Opens an ADF device. + * + * On error, returns -errno. + */ +int adf_device_open(adf_id_t id, int flags, struct adf_device *dev); +/** + * Closes an ADF device. + */ +void adf_device_close(struct adf_device *dev); +/** + * Reads the ADF device data. + * + * adf_get_device_data() allocates buffers inside data, which the caller + * must free by calling adf_free_device_data(). On error, returns -errno. + */ +int adf_get_device_data(struct adf_device *dev, struct adf_device_data *data); +/** + * Frees the device data returned by adf_get_device_data(). + */ +void adf_free_device_data(struct adf_device_data *data); + +/** + * Atomically posts a new display configuration to the specified interfaces. + * + * Returns a sync fence fd that will fire when the configuration is removed + * from the screen. On error, returns -errno. + */ +int adf_device_post(struct adf_device *dev, + adf_id_t *interfaces, size_t n_interfaces, + struct adf_buffer_config *bufs, size_t n_bufs, + void *custom_data, size_t custom_data_size); +/** + * Attaches the specified interface and overlay engine. + */ +int adf_device_attach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface); +/** + * Detaches the specified interface and overlay engine. + */ +int adf_device_detach(struct adf_device *dev, adf_id_t overlay_engine, + adf_id_t interface); + +/** + * Enumerates all interfaces belonging to an ADF device. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces(struct adf_device *dev, adf_id_t **interfaces); + +/** + * Enumerates all interfaces which can be attached to the specified overlay + * engine. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces_for_overlay_engine(struct adf_device *dev, + adf_id_t overlay_engine, adf_id_t **interfaces); +/** + * Filters a list of interfaces by type. + * + * Returns the number of matching interfaces, and sets out to a list of matching + * interface IDs. The caller must free() the returned list of interface IDs. + * + * On error, returns -errno. + */ +ssize_t adf_interfaces_filter_by_type(struct adf_device *dev, + enum adf_interface_type type, + adf_id_t *in, size_t n_in, adf_id_t **out); +/** + * Filters a list of interfaces by flag. + * + * The caller must free() the returned list of interface IDs. + */ +ssize_t adf_interfaces_filter_by_flag(struct adf_device *dev, __u32 flag, + adf_id_t *in, size_t n_in, adf_id_t **out); + +/** + * Opens an ADF interface. + * + * Returns a file descriptor. The caller must close() the fd when done. + * On error, returns -errno. + */ +int adf_interface_open(struct adf_device *dev, adf_id_t id, int flags); +/** + * Reads the interface data. + * + * adf_get_interface_data() allocates buffers inside data, which the caller + * must free by calling adf_free_interface_data(). On error, returns -errno. + */ +int adf_get_interface_data(int fd, struct adf_interface_data *data); +/** + * Frees the interface data returned by adf_get_interface_data(). + */ +void adf_free_interface_data(struct adf_interface_data *data); + +/** + * Sets the interface's DPMS mode. + */ +int adf_interface_blank(int fd, __u8 mode); +/** + * Sets the interface's display mode. + */ +int adf_interface_set_mode(int fd, struct drm_mode_modeinfo *mode); +/** + * Allocates a single-plane RGB buffer of the specified size and format. + * + * Returns a dma-buf fd. On error, returns -errno. + */ +int adf_interface_simple_buffer_alloc(int fd, __u32 w, __u32 h, + __u32 format, __u32 *offset, __u32 *pitch); +/** + * Posts a single-plane RGB buffer to the display using the specified + * overlay engine. + * + * Returns a sync fence fd that will fire when the buffer is removed + * from the screen. On error, returns -errno. + */ +int adf_interface_simple_post(int fd, adf_id_t overlay_engine, + __u32 w, __u32 h, __u32 format, int buf_fd, __u32 offset, + __u32 pitch, int acquire_fence); + +/** + * Enumerates all overlay engines belonging to an ADF device. + * + * The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines(struct adf_device *dev, adf_id_t **overlay_engines); + +/** + * Enumerates all overlay engines which can be attached to the specified + * interface. + * + * The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines_for_interface(struct adf_device *dev, + adf_id_t interface, adf_id_t **overlay_engines); +/** + * Filters a list of overlay engines by supported buffer format. + * + * Returns the overlay engines which support at least one of the specified + * formats. The caller must free() the returned list of overlay engine IDs. + */ +ssize_t adf_overlay_engines_filter_by_format(struct adf_device *dev, + const __u32 *formats, size_t n_formats, adf_id_t *in, size_t n_in, + adf_id_t **out); + +/** + * Opens an ADF overlay engine. + * + * Returns a file descriptor. The caller must close() the fd when done. + * On error, returns -errno. + */ +int adf_overlay_engine_open(struct adf_device *dev, adf_id_t id, int flags); +/** + * Reads the overlay engine data. + * + * adf_get_overlay_engine_data() allocates buffers inside data, which the caller + * must free by calling adf_free_overlay_engine_data(). On error, returns + * -errno. + */ +int adf_get_overlay_engine_data(int fd, struct adf_overlay_engine_data *data); +/** + * Frees the overlay engine data returned by adf_get_overlay_engine_data(). + */ +void adf_free_overlay_engine_data(struct adf_overlay_engine_data *data); + +/** + * Returns whether the overlay engine supports the specified format. + */ +bool adf_overlay_engine_supports_format(int fd, __u32 format); + +/** + * Subscribes or unsubscribes from the specified hardware event. + */ +int adf_set_event(int fd, enum adf_event_type type, bool enabled); +/** + * Reads one event from the fd, blocking if needed. + * + * The caller must free() the returned buffer. On error, returns -errno. + */ +int adf_read_event(int fd, struct adf_event **event); + +#define ADF_FORMAT_STR_SIZE 5 +/** + * Converts an ADF/DRM fourcc format to its string representation. + */ +void adf_format_str(__u32 format, char buf[ADF_FORMAT_STR_SIZE]); + +/** + * Finds an appropriate interface and overlay engine for a simple post. + * + * Specifically, finds the primary interface, and an overlay engine + * that can be attached to the primary interface and supports one of the + * specified formats. The caller may pass a NULL formats list, to indicate that + * any RGB format is acceptable. + * + * On error, returns -errno. + */ +int adf_find_simple_post_configuration(struct adf_device *dev, + const __u32 *formats, size_t n_formats, + adf_id_t *interface, adf_id_t *overlay_engine); + +__END_DECLS + +#endif /* _LIBADF_ADF_H_ */ diff --git a/adf/libadfhwc/Android.mk b/adf/libadfhwc/Android.mk new file mode 100644 index 0000000..acea322 --- /dev/null +++ b/adf/libadfhwc/Android.mk @@ -0,0 +1,25 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := adfhwc.cpp +LOCAL_MODULE := libadfhwc +LOCAL_MODULE_TAGS := optional +LOCAL_STATIC_LIBRARIES := libadf liblog libutils +LOCAL_CFLAGS += -DLOG_TAG=\"adfhwc\" +LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include +LOCAL_C_INCLUDES += $(LOCAL_EXPORT_C_INCLUDE_DIRS) +include $(BUILD_STATIC_LIBRARY) diff --git a/adf/libadfhwc/adfhwc.cpp b/adf/libadfhwc/adfhwc.cpp new file mode 100644 index 0000000..dee3cae --- /dev/null +++ b/adf/libadfhwc/adfhwc.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2013 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 <fcntl.h> +#include <poll.h> +#include <pthread.h> +#include <sys/resource.h> + +#include <adf/adf.h> +#include <adfhwc/adfhwc.h> + +#include <cutils/log.h> +#include <utils/Vector.h> + +struct adf_hwc_helper { + adf_hwc_event_callbacks const *event_cb; + void *event_cb_data; + + pthread_t event_thread; + + android::Vector<int> intf_fds; + android::Vector<drm_mode_modeinfo> display_configs; +}; + +template<typename T> inline T min(T a, T b) { return (a < b) ? a : b; } + +int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event, + int enabled) +{ + if (enabled != !!enabled) + return -EINVAL; + + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + switch (event) { + case HWC_EVENT_VSYNC: + return adf_set_event(dev->intf_fds[disp], ADF_EVENT_VSYNC, enabled); + } + + return -EINVAL; +} + +static inline int32_t dpi(uint16_t res, uint16_t size_mm) +{ + if (size_mm) + return 1000 * (res * 25.4f) / size_mm; + return 0; +} + +int adf_blank(struct adf_hwc_helper *dev, int disp, int blank) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + uint8_t dpms_mode = blank ? DRM_MODE_DPMS_OFF : DRM_MODE_DPMS_ON; + return adf_interface_blank(dev->intf_fds[disp], dpms_mode); +} + +int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value) +{ + *value = 0; + if (dev->intf_fds.size() > 0) + *value |= HWC_DISPLAY_PRIMARY_BIT; + if (dev->intf_fds.size() > 1) + *value |= HWC_DISPLAY_EXTERNAL_BIT; + + return 0; +} + +int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp, + uint32_t *configs, size_t *numConfigs) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + adf_interface_data data; + int err = adf_get_interface_data(dev->intf_fds[disp], &data); + if (err < 0) { + ALOGE("failed to get ADF interface data: %s", strerror(err)); + return err; + } + + if (!data.hotplug_detect) + return -ENODEV; + + android::Vector<drm_mode_modeinfo *> unique_configs; + unique_configs.push_back(&data.current_mode); + for (size_t i = 0; i < data.n_available_modes; i++) + if (memcmp(&data.available_modes[i], &data.current_mode, + sizeof(data.current_mode))) + unique_configs.push_back(&data.available_modes[i]); + + for (size_t i = 0; i < min(*numConfigs, unique_configs.size()); i++) { + configs[i] = dev->display_configs.size(); + dev->display_configs.push_back(*unique_configs[i]); + } + *numConfigs = unique_configs.size(); + + adf_free_interface_data(&data); + return 0; +} + +static int32_t adf_display_attribute(const adf_interface_data &data, + const drm_mode_modeinfo &mode, const uint32_t attribute) +{ + switch (attribute) { + case HWC_DISPLAY_VSYNC_PERIOD: + if (mode.vrefresh) + return 1000000000 / mode.vrefresh; + return 0; + + case HWC_DISPLAY_WIDTH: + return mode.hdisplay; + + case HWC_DISPLAY_HEIGHT: + return mode.vdisplay; + + case HWC_DISPLAY_DPI_X: + return dpi(mode.hdisplay, data.width_mm); + + case HWC_DISPLAY_DPI_Y: + return dpi(mode.vdisplay, data.height_mm); + + default: + ALOGE("unknown display attribute %u", attribute); + return -EINVAL; + } +} + +int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp, + uint32_t config, const uint32_t *attributes, int32_t *values) +{ + if ((size_t)disp >= dev->intf_fds.size()) + return -EINVAL; + + if (config >= dev->display_configs.size()) + return -EINVAL; + + adf_interface_data data; + int err = adf_get_interface_data(dev->intf_fds[disp], &data); + if (err < 0) { + ALOGE("failed to get ADF interface data: %s", strerror(err)); + return err; + } + + for (int i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) + values[i] = adf_display_attribute(data, dev->display_configs[config], + attributes[i]); + + adf_free_interface_data(&data); + return 0; +} + +static void handle_adf_event(struct adf_hwc_helper *dev, int disp) +{ + adf_event *event; + int err = adf_read_event(dev->intf_fds[disp], &event); + if (err < 0) { + ALOGE("error reading event from display %d: %s", disp, strerror(err)); + return; + } + + void *vsync_temp; + adf_vsync_event *vsync; + adf_hotplug_event *hotplug; + + switch (event->type) { + case ADF_EVENT_VSYNC: + vsync_temp = event; + vsync = static_cast<adf_vsync_event *>(vsync_temp); + // casting directly to adf_vsync_event * makes g++ warn about + // potential alignment issues that don't apply here + dev->event_cb->vsync(dev->event_cb_data, disp, vsync->timestamp); + break; + case ADF_EVENT_HOTPLUG: + hotplug = reinterpret_cast<adf_hotplug_event *>(event); + dev->event_cb->hotplug(dev->event_cb_data, disp, hotplug->connected); + break; + default: + if (event->type < ADF_EVENT_DEVICE_CUSTOM) + ALOGW("unrecognized event type %u", event->type); + else if (!dev->event_cb || !dev->event_cb->custom_event) + ALOGW("unhandled event type %u", event->type); + else + dev->event_cb->custom_event(dev->event_cb_data, disp, event); + } + free(event); +} + +static void *adf_event_thread(void *data) +{ + adf_hwc_helper *dev = static_cast<adf_hwc_helper *>(data); + + setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + + pollfd *fds = new pollfd[dev->intf_fds.size()]; + for (size_t i = 0; i < dev->intf_fds.size(); i++) { + fds[i].fd = dev->intf_fds[i]; + fds[i].events = POLLIN | POLLPRI; + } + + while (true) { + int err = poll(fds, dev->intf_fds.size(), -1); + + if (err > 0) { + for (size_t i = 0; i < dev->intf_fds.size(); i++) + if (fds[i].revents & (POLLIN | POLLPRI)) + handle_adf_event(dev, i); + } + else if (err == -1) { + if (errno == EINTR) + break; + ALOGE("error in event thread: %s", strerror(errno)); + } + } + + delete [] fds; + return NULL; +} + +int adf_hwc_open(int *intf_fds, size_t n_intfs, + const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data, + struct adf_hwc_helper **dev) +{ + if (!n_intfs) + return -EINVAL; + + adf_hwc_helper *dev_ret = new adf_hwc_helper; + dev_ret->event_cb = event_cb; + dev_ret->event_cb_data = event_cb_data; + + int ret; + + for (size_t i = 0; i < n_intfs; i++) { + int dup_intf_fd = dup(intf_fds[i]); + if (dup_intf_fd < 0) { + ALOGE("failed to dup interface fd: %s", strerror(errno)); + ret = -errno; + goto err; + } + + dev_ret->intf_fds.push_back(dup_intf_fd); + + ret = adf_set_event(dup_intf_fd, ADF_EVENT_HOTPLUG, 1); + if (ret < 0 && ret != -EINVAL) { + ALOGE("failed to enable hotplug event on display %u: %s", + i, strerror(errno)); + goto err; + } + } + + ret = pthread_create(&dev_ret->event_thread, NULL, adf_event_thread, + dev_ret); + if (ret) { + ALOGE("failed to create event thread: %s", strerror(ret)); + goto err; + } + + *dev = dev_ret; + return 0; + +err: + for (size_t i = 0; i < dev_ret->intf_fds.size(); i++) + close(dev_ret->intf_fds[i]); + + delete dev_ret; + return ret; +} + +void adf_hwc_close(struct adf_hwc_helper *dev) +{ + pthread_kill(dev->event_thread, SIGTERM); + pthread_join(dev->event_thread, NULL); + + for (size_t i = 0; i < dev->intf_fds.size(); i++) + close(dev->intf_fds[i]); + + delete dev; +} diff --git a/adf/libadfhwc/include/adfhwc/adfhwc.h b/adf/libadfhwc/include/adfhwc/adfhwc.h new file mode 100644 index 0000000..71e7624 --- /dev/null +++ b/adf/libadfhwc/include/adfhwc/adfhwc.h @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2013 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. + */ + +#ifndef _LIBADFHWC_ADFHWC_H_ +#define _LIBADFHWC_ADFHWC_H_ + +#include <stdbool.h> +#include <stdint.h> +#include <sys/cdefs.h> +#include <video/adf.h> + +#include <hardware/hwcomposer.h> + +struct adf_hwc_helper; + +struct adf_hwc_event_callbacks { + /** + * Called on vsync (required) + */ + void (*vsync)(void *data, int disp, uint64_t timestamp); + /** + * Called on hotplug (required) + */ + void (*hotplug)(void *data, int disp, bool connected); + /** + * Called on hardware-custom ADF events (optional) + */ + void (*custom_event)(void *data, int disp, struct adf_event *event); +}; + +/** + * Converts HAL pixel formats to equivalent ADF/DRM format FourCCs. + */ +static inline uint32_t adf_fourcc_for_hal_pixel_format(int format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + return DRM_FORMAT_RGBA8888; + case HAL_PIXEL_FORMAT_RGBX_8888: + return DRM_FORMAT_RGBX8888; + case HAL_PIXEL_FORMAT_RGB_888: + return DRM_FORMAT_RGB888; + case HAL_PIXEL_FORMAT_RGB_565: + return DRM_FORMAT_RGB565; + case HAL_PIXEL_FORMAT_BGRA_8888: + return DRM_FORMAT_BGRA8888; + case HAL_PIXEL_FORMAT_YV12: + return DRM_FORMAT_YVU420; + case HAL_PIXEL_FORMAT_YCbCr_422_SP: + return DRM_FORMAT_NV16; + case HAL_PIXEL_FORMAT_YCrCb_420_SP: + return DRM_FORMAT_NV21; + case HAL_PIXEL_FORMAT_YCbCr_422_I: + return DRM_FORMAT_YUYV; + default: + return 0; + } +} + +/** + * Converts HAL display types to equivalent ADF interface flags. + */ +static inline uint32_t adf_hwc_interface_flag_for_disp(int disp) +{ + switch (disp) { + case HWC_DISPLAY_PRIMARY: + return ADF_INTF_FLAG_PRIMARY; + case HWC_DISPLAY_EXTERNAL: + return ADF_INTF_FLAG_EXTERNAL; + default: + return 0; + } +} + +__BEGIN_DECLS + +/** + * Create a HWC helper for the specified ADF interfaces. + * + * intf_fds must be indexed by HWC display type: e.g., + * intf_fds[HWC_DISPLAY_PRIMARY] is the fd for the primary display + * interface. n_intfs must be >= 1. + * + * The caller retains ownership of the fds in intf_fds and must close() + * them when they are no longer needed. + * + * On error, returns -errno. + */ +int adf_hwc_open(int *intf_fds, size_t n_intfs, + const struct adf_hwc_event_callbacks *event_cb, void *event_cb_data, + struct adf_hwc_helper **dev); + +/** + * Destroys a HWC helper. + */ +void adf_hwc_close(struct adf_hwc_helper *dev); + +/** + * Generic implementations of common HWC ops. + * + * The HWC should not point its ops directly at these helpers. Instead, the HWC + * should provide stub ops which call these helpers after converting the + * hwc_composer_device_1* to a struct adf_hwc_helper*. + */ +int adf_eventControl(struct adf_hwc_helper *dev, int disp, int event, + int enabled); +int adf_blank(struct adf_hwc_helper *dev, int disp, int blank); +int adf_query_display_types_supported(struct adf_hwc_helper *dev, int *value); +int adf_getDisplayConfigs(struct adf_hwc_helper *dev, int disp, + uint32_t *configs, size_t *numConfigs); +int adf_getDisplayAttributes(struct adf_hwc_helper *dev, int disp, + uint32_t config, const uint32_t *attributes, int32_t *values); + +__END_DECLS + +#endif /* _LIBADFHWC_ADFHWC_H_ */ |