diff options
-rw-r--r-- | libhwcomposer/Android.bp | 44 | ||||
-rw-r--r-- | libhwcomposer/NOTICE | 203 | ||||
-rw-r--r-- | libhwcomposer/display.cpp | 273 | ||||
-rw-r--r-- | libhwcomposer/display.h | 126 | ||||
-rw-r--r-- | libhwcomposer/drmfb.cpp | 100 | ||||
-rw-r--r-- | libhwcomposer/drmfb.h | 35 | ||||
-rw-r--r-- | libhwcomposer/format.cpp | 152 | ||||
-rw-r--r-- | libhwcomposer/format.h | 63 | ||||
-rw-r--r-- | libhwcomposer/hal_public.h | 245 | ||||
-rw-r--r-- | libhwcomposer/hwc.cpp | 724 | ||||
-rw-r--r-- | libhwcomposer/hwc_dev.h | 57 |
11 files changed, 2022 insertions, 0 deletions
diff --git a/libhwcomposer/Android.bp b/libhwcomposer/Android.bp new file mode 100644 index 0000000..384e7e2 --- /dev/null +++ b/libhwcomposer/Android.bp @@ -0,0 +1,44 @@ +// Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/ +// +// 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. + +cc_library_shared { + name: "hwcomposer.am57x", + + vendor: true, + relative_install_path: "hw", + + srcs: [ + "display.cpp", + "drmfb.cpp", + "format.cpp", + "hwc.cpp", + ], + + cflags: [ + "-DLOG_TAG=\"ti_hwc\"", + "-Wno-unused-parameter", + "-Wno-missing-field-initializers", + "-fexceptions", + ], + + shared_libs: [ + "liblog", + "libcutils", + "libutils", + "libhardware", + "libhardware_legacy", + "libdrm", + "libkmsxx", + ], +} diff --git a/libhwcomposer/NOTICE b/libhwcomposer/NOTICE new file mode 100644 index 0000000..6b0b127 --- /dev/null +++ b/libhwcomposer/NOTICE @@ -0,0 +1,203 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + diff --git a/libhwcomposer/display.cpp b/libhwcomposer/display.cpp new file mode 100644 index 0000000..f881a84 --- /dev/null +++ b/libhwcomposer/display.cpp @@ -0,0 +1,273 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 <algorithm> + +#include <cstdint> + +#include <cutils/log.h> +#include <cutils/properties.h> + +#include "display.h" +#include "format.h" +#include "hwc_dev.h" + +HWCDisplay::HWCDisplay(enum disp_role role) : + active_config(0), + role(role), + cb_procs(NULL), + is_dummy(false), + vsync_on(false), + blanked(true), + is_flip_pending(false) +{ +} + +void HWCDisplay::setup_composition_pipes() +{ + std::unique_lock<std::mutex> lock(this->mutex); + + KMSDisplay* kdisp = &this->disp_link; + + int count = 0; + for (auto plane : kdisp->card->get_planes()) { + auto possible_crtcs = plane->get_possible_crtcs(); + if (std::find(possible_crtcs.begin(), possible_crtcs.end(), kdisp->crtc) != possible_crtcs.end()) { + planeProps[count].plane = plane; + ALOGI("Overlay %d: plane_id: %d", count, planeProps[count].plane->id()); + count++; + } + } +} + +/* Page flip handler callback */ +void HWCDisplay::page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) +{ + HWCDisplay* display = static_cast<HWCDisplay*>(data); + + std::unique_lock<std::mutex> lock(display->mutex); + + if (display->is_flip_pending == false) { + ALOGW("Spurious page flip?"); + return; + } + + /* release the old buffers */ + for (auto current_fb_info : display->current_fb_infos) + delete current_fb_info; + display->current_fb_infos.clear(); + + /* pending are now current */ + for (auto pending_fb_info : display->pending_fb_infos) + display->current_fb_infos.push_back(pending_fb_info); + display->pending_fb_infos.clear(); + + display->is_flip_pending = false; + lock.unlock(); + display->cond_flip.notify_one(); +} + +static int vblank_kick(HWCDisplay* display) +{ + drmVBlank vblank; + memset(&vblank, 0, sizeof(vblank)); + vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT | + display->role == DISP_ROLE_SECONDARY ? DRM_VBLANK_SECONDARY : 0); + vblank.request.sequence = 1; + vblank.request.signal = (unsigned long)display; + int err = drmWaitVBlank(display->disp_link.card->fd(), &vblank); + if (err < 0) { + /* FIXME: error in drmWaitVBlank() use SW vsync instead? */ + ALOGE("drmWaitVBlank error %d (%s)", err, strerror(errno)); + return -1; + } + + return 0; +} + +/* Callback function that gets triggered on vsync */ +void HWCDisplay::vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) +{ + HWCDisplay* display = static_cast<HWCDisplay*>(data); + + std::unique_lock<std::mutex> lock(display->mutex); + + if (display->vsync_on) { + int64_t ts = sec * (int64_t)1000000000 + usec * (int64_t)1000; + +// ALOGD("Sending VBLANK at %lld for display %d", ts, display->role); + display->cb_procs->vsync(display->cb_procs, display->role, ts); + + vblank_kick(display); + } +} + +int HWCDisplay::set_vsync_state(bool state) +{ + std::unique_lock<std::mutex> lock(this->mutex); + + if (this->is_dummy) + return 0; + + this->vsync_on = state; + + if (this->vsync_on) + return vblank_kick(this); + + return 0; +} + +static void set_plane_properties(kms::AtomicReq& req, drm_plane_props_t* plane_props) +{ + kms::Plane* plane = plane_props->plane; + + req.add(plane, "FB_ID", plane_props->fb_info->fb_id); + + req.add(plane, "IN_FENCE_FD", plane_props->layer->acquireFenceFd); + + req.add(plane, { + { "CRTC_ID", plane_props->crtc_id }, + { "SRC_X", (plane_props->src_x) << 16 }, + { "SRC_Y", (plane_props->src_y) << 16 }, + { "SRC_W", (plane_props->src_w) << 16 }, + { "SRC_H", (plane_props->src_h) << 16 }, + { "CRTC_X", plane_props->crtc_x }, + { "CRTC_Y", plane_props->crtc_y }, + { "CRTC_W", plane_props->crtc_w }, + { "CRTC_H", plane_props->crtc_h }, + }); + + /* optional props */ + req.add(plane, { + { "zorder", plane_props->zorder }, + { "pre_mult_alpha", plane_props->pre_mult_alpha }, + }); +} + +int HWCDisplay::update_display(drm_plane_props_t* planeProp) +{ + std::unique_lock<std::mutex> lock(this->mutex); + + this->cond_flip.wait(lock, [this]{return !this->is_flip_pending;}); + + buffer_handle_t handle = planeProp->layer->handle; + if (!handle) { + ALOGW("Got empty handle, nothing to display"); + return 0; + } + + KMSDisplay* kdisp = &this->disp_link; + + planeProp->fb_info = new DRMFramebuffer(kdisp->card->fd(), handle, false); + this->pending_fb_infos.push_back(planeProp->fb_info); + + planeProp->crtc_id = kdisp->crtc->id(); + + // Atomic display update + kms::AtomicReq req(*kdisp->card); + set_plane_properties(req, planeProp); + int ret = req.commit(this, true); + if (ret) { + ALOGE("cannot do atomic commit/page flip: %d (%s)", ret, strerror(errno)); + for (auto pending_fb_info : this->pending_fb_infos) + delete pending_fb_info; + this->pending_fb_infos.clear(); + return ret; + } + +// ALOGD("Scheduled page flip on display %d", this->role); + this->is_flip_pending = true; + + return 0; +} + +void HWCDisplay::blank(int blank) +{ + std::unique_lock<std::mutex> lock(this->mutex); + + if (this->is_dummy) + return; + + KMSDisplay* kdisp = &this->disp_link; + + ALOGI("Linking connector %d to crtc %d", kdisp->con->id(), kdisp->crtc->id()); + + int ret = kdisp->crtc->set_mode(kdisp->con, kdisp->mode); + if (ret) { + ALOGE("Failed to set crtc mode (%s)", strerror(-ret)); + return; + } + + // FIXME: This should actually blank the screen + this->blanked = blank; +} + +int HWCDisplay::get_display_configs(uint32_t* configs, size_t* numConfigs) +{ + if (!configs || !numConfigs) + return -EINVAL; + + if (*numConfigs == 0) + return 0; + + std::unique_lock<std::mutex> lock(this->mutex); + + size_t num = this->configs.size(); + if (num > *numConfigs) + num = *numConfigs; + + for (size_t i = 0; i < num; i++) + configs[i] = i; + + *numConfigs = num; + + return 0; +} + +int HWCDisplay::get_display_attributes(uint32_t cfg, const uint32_t* attributes, int32_t* values) +{ + if (!attributes || !values) + return 0; + + std::unique_lock<std::mutex> lock(this->mutex); + + if (cfg >= this->configs.size()) + return -EINVAL; + + display_config_t* config = &this->configs[cfg]; + + for (size_t i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) { + switch (attributes[i]) { + case HWC_DISPLAY_VSYNC_PERIOD: + values[i] = 1000000000 / config->fps; + break; + case HWC_DISPLAY_WIDTH: + values[i] = config->xres; + break; + case HWC_DISPLAY_HEIGHT: + values[i] = config->yres; + break; + case HWC_DISPLAY_DPI_X: + values[i] = 1000 * config->xdpi; + break; + case HWC_DISPLAY_DPI_Y: + values[i] = 1000 * config->ydpi; + break; + } + } + + return 0; +} diff --git a/libhwcomposer/display.h b/libhwcomposer/display.h new file mode 100644 index 0000000..5df923f --- /dev/null +++ b/libhwcomposer/display.h @@ -0,0 +1,126 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +#pragma once + +#include <condition_variable> +#include <mutex> + +#include <cstdbool> +#include <cstdint> + +#include <hardware/hwcomposer.h> +#include <kms++/kms++.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "drmfb.h" + +#define MAX_DISPLAYS 4 +#define DSS_AVAILABLE_PIPES 4 + +typedef struct display_config { + unsigned int xres; + unsigned int yres; + unsigned int fps; + unsigned int xdpi; + unsigned int ydpi; +} display_config_t; + +enum disp_role { + DISP_ROLE_PRIMARY = 0, + DISP_ROLE_SECONDARY, +}; + +typedef struct drm_plane_props { + hwc_layer_1_t* layer; + + kms::Plane* plane; + uint64_t crtc_id; + + uint64_t crtc_x; + uint64_t crtc_y; + uint64_t crtc_w; + uint64_t crtc_h; + uint64_t src_x; + uint64_t src_y; + uint64_t src_w; + uint64_t src_h; + + uint64_t rotation; + uint64_t zorder; + uint64_t pre_mult_alpha; + + DRMFramebuffer* fb_info; +} drm_plane_props_t; + +class KMSDisplay { +public: + KMSDisplay() : + card(NULL), + con(NULL), + crtc(NULL), + mode() {} + + kms::Card* card; + kms::Connector* con; + kms::Crtc* crtc; + kms::Videomode mode; +}; + +class HWCDisplay +{ +public: + HWCDisplay(enum disp_role role); + ~HWCDisplay(){}; + + void setup_composition_pipes(); + + int update_display(drm_plane_props_t* planeProp); + + std::vector<display_config_t> configs; + uint32_t active_config; + + enum disp_role role; + + drm_plane_props_t planeProps[DSS_AVAILABLE_PIPES]; + + KMSDisplay disp_link; + + const hwc_procs_t* cb_procs; + + bool is_dummy; + + int set_vsync_state(bool state); + void blank(int blank); + int get_display_configs(uint32_t* configs, size_t* numConfigs); + int get_display_attributes(uint32_t cfg, const uint32_t* attributes, int32_t* values); + + static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data); + static void vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data); + +private: + std::vector<DRMFramebuffer*> pending_fb_infos; + std::vector<DRMFramebuffer*> current_fb_infos; + + bool vsync_on; + bool blanked; + + std::mutex mutex; + std::condition_variable cond_flip; + volatile bool is_flip_pending; +}; diff --git a/libhwcomposer/drmfb.cpp b/libhwcomposer/drmfb.cpp new file mode 100644 index 0000000..04c41dd --- /dev/null +++ b/libhwcomposer/drmfb.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 <cutils/log.h> +#include <drm/drm_fourcc.h> +#include <hardware/hwcomposer.h> + +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include "drmfb.h" +#include "format.h" +#include "hal_public.h" + +DRMFramebuffer::DRMFramebuffer(int drm_fd, buffer_handle_t handle, bool is_overlay) : + bo(), pitches(), offsets() +{ + if (!handle) + return; + + uint32_t gem_handle; + IMG_native_handle_t* img_hnd = (IMG_native_handle_t*)handle; + int ret = drmPrimeFDToHandle(drm_fd, img_hnd->fd[0], &gem_handle); + if (ret) { + ALOGE("Failed to get DRM buffer object from handle"); + return; + } + + this->width = img_hnd->iWidth; + this->height = img_hnd->iHeight; + this->format = convert_hal_to_drm_format(img_hnd->iFormat, true); + this->bo[0] = gem_handle; + this->pitches[0] = ALIGN(img_hnd->iWidth, HW_ALIGN) * get_format_bpp(img_hnd->iFormat) >> 3; + this->offsets[0] = 0; + this->drm_fd = drm_fd; + + if (is_overlay) { + switch (this->format) { + case DRM_FORMAT_NV12: + this->bo[1] = gem_handle; + + this->pitches[0] = ALIGN(img_hnd->iWidth, HW_ALIGN); + this->pitches[1] = this->pitches[0]; + + this->offsets[1] = this->pitches[0] * img_hnd->iHeight; + break; + case DRM_FORMAT_ARGB8888: + case DRM_FORMAT_XRGB8888: + case DRM_FORMAT_RGB565: + break; + default: + ALOGE("Bad format for overlay"); + return; + } + } + + ret = drmModeAddFB2(drm_fd, this->width, this->height, + this->format, this->bo, + this->pitches, this->offsets, + &this->fb_id, 0); + if (ret) { + ALOGE("Could not create DRM frame buffer %d", ret); + return; + } +} + +DRMFramebuffer::~DRMFramebuffer() +{ + if (this->fb_id) { + if (drmModeRmFB(this->drm_fd, this->fb_id)) + ALOGE("Failed to remove DRM frame buffer"); + } + + for (size_t i = 0; i < 4; i++) { + if (this->bo[i]) { + struct drm_gem_close close_args = { + close_args.handle = this->bo[i], + close_args.pad = 0, + }; + int ret = drmIoctl(this->drm_fd, DRM_IOCTL_GEM_CLOSE, &close_args); + if (ret) { + ALOGE("Failed to close GEM handle"); + return; + } + } + } +} diff --git a/libhwcomposer/drmfb.h b/libhwcomposer/drmfb.h new file mode 100644 index 0000000..1efff0d --- /dev/null +++ b/libhwcomposer/drmfb.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +#pragma once + +#include <cstdint> + +class DRMFramebuffer +{ + public: + DRMFramebuffer(int drm_fd, buffer_handle_t handle, bool is_overlay); + ~DRMFramebuffer(); + + uint32_t width; + uint32_t height; + uint32_t format; + uint32_t bo[4]; + uint32_t pitches[4]; + uint32_t offsets[4]; + uint32_t fb_id; + int drm_fd; +}; diff --git a/libhwcomposer/format.cpp b/libhwcomposer/format.cpp new file mode 100644 index 0000000..7f39203 --- /dev/null +++ b/libhwcomposer/format.cpp @@ -0,0 +1,152 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 <cstdint> + +#include <cutils/log.h> + +#include <linux/types.h> + +#include "format.h" +#include "hal_public.h" + +bool is_valid_format(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + return true; + + case HAL_PIXEL_FORMAT_RGB_565: + return true; + + case HAL_PIXEL_FORMAT_NV12: + return true; + + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + return true; + + case HAL_PIXEL_FORMAT_TI_NV12: + case HAL_PIXEL_FORMAT_TI_NV12_1D: + /* legacy formats not supported anymore */ + return false; + + default: + return false; + } +} + +bool is_rgb_format(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGB_565: + return true; + default: + return false; + } +} + +bool is_bgr_format(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + return true; + default: + return false; + } +} + +bool is_nv12_format(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_TI_NV12: + case HAL_PIXEL_FORMAT_TI_NV12_1D: + case HAL_PIXEL_FORMAT_NV12: + return true; + default: + return false; + } +} + +bool is_opaque_format(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_RGB_565: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + return true; + default: + return false; + } +} + +uint32_t get_format_bpp(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_BGRA_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_RGBA_8888: + return 32; + case HAL_PIXEL_FORMAT_RGB_565: + return 16; + case HAL_PIXEL_FORMAT_TI_NV12: + case HAL_PIXEL_FORMAT_TI_NV12_1D: + case HAL_PIXEL_FORMAT_NV12: + return 8; + default: + return 0; + } +} + +uint32_t convert_hal_to_drm_format(uint32_t hal_format, bool blended) +{ + uint32_t dss_format = HAL_PIXEL_FORMAT_RGBA_8888; + + /* convert color format */ + switch (hal_format) { + case HAL_PIXEL_FORMAT_RGBA_8888: + case HAL_PIXEL_FORMAT_BGRA_8888: + dss_format = DRM_FORMAT_ARGB8888; + if (blended) + break; + + case HAL_PIXEL_FORMAT_RGBX_8888: + case HAL_PIXEL_FORMAT_BGRX_8888: + dss_format = DRM_FORMAT_XRGB8888; + break; + + case HAL_PIXEL_FORMAT_RGB_565: + dss_format = DRM_FORMAT_RGB565; + break; + + case HAL_PIXEL_FORMAT_TI_NV12: + case HAL_PIXEL_FORMAT_TI_NV12_1D: + case HAL_PIXEL_FORMAT_NV12: + dss_format = DRM_FORMAT_NV12; + break; + + default: + /* Should have been filtered out */ + ALOGV("Unsupported pixel format"); + } + + return dss_format; +} diff --git a/libhwcomposer/format.h b/libhwcomposer/format.h new file mode 100644 index 0000000..731c53a --- /dev/null +++ b/libhwcomposer/format.h @@ -0,0 +1,63 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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. + */ + +#pragma once + +#include <string> + +#include <cstdint> + +#include <drm/drm_fourcc.h> +#include <linux/types.h> + +#include "hal_public.h" + +static inline std::string HAL_FMT(uint32_t format) +{ + switch (format) { + case HAL_PIXEL_FORMAT_TI_NV12: return "NV12"; + case HAL_PIXEL_FORMAT_TI_NV12_1D: return "NV12"; + case HAL_PIXEL_FORMAT_YV12: return "YV12"; + case HAL_PIXEL_FORMAT_BGRX_8888: return "xRGB32"; + case HAL_PIXEL_FORMAT_RGBX_8888: return "xBGR32"; + case HAL_PIXEL_FORMAT_BGRA_8888: return "ARGB32"; + case HAL_PIXEL_FORMAT_RGBA_8888: return "ABGR32"; + case HAL_PIXEL_FORMAT_RGB_565: return "RGB565"; + default: return "??"; + } +} + +static inline std::string DRM_FMT(uint32_t format) +{ + switch (format) { + case DRM_FORMAT_NV12: return "NV12"; + case DRM_FORMAT_XRGB8888: return "xRGB32"; + case DRM_FORMAT_ARGB8888: return "ARGB32"; + case DRM_FORMAT_RGB565: return "RGB565"; + default: return "??"; + } +} + +bool is_valid_format(uint32_t format); +bool is_rgb_format(uint32_t format); +bool is_bgr_format(uint32_t format); +bool is_nv12_format(uint32_t format); +bool is_opaque_format(uint32_t format); +uint32_t get_format_bpp(uint32_t format); +uint32_t convert_hal_to_dss_format(uint32_t format, bool blended); +uint32_t convert_hal_to_drm_format(uint32_t hal_format, bool blended); +uint32_t convert_hal_to_ocd_format(uint32_t hal_format); +uint32_t get_stride_from_format(uint32_t format, uint32_t width); diff --git a/libhwcomposer/hal_public.h b/libhwcomposer/hal_public.h new file mode 100644 index 0000000..5ca9bd2 --- /dev/null +++ b/libhwcomposer/hal_public.h @@ -0,0 +1,245 @@ +/* Copyright (c) Imagination Technologies Ltd. + * + * The contents of this file are subject to the MIT license as set out below. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#ifndef HAL_PUBLIC_H +#define HAL_PUBLIC_H + +/* Authors of third party hardware composer (HWC) modules will need to include + * this header to access functionality in the gralloc and framebuffer HALs. + */ + +#include <hardware/gralloc.h> +#include <hardware/memtrack.h> + +#define ALIGN(x,a) (((x) + (a) - 1L) & ~((a) - 1L)) +#define HW_ALIGN 16 + +/* This can be tuned down as appropriate for the SOC. + * + * IMG formats are usually a single sub-alloc. + * Some OEM video formats are two sub-allocs (Y, UV planes). + * Future OEM video formats might be three sub-allocs (Y, U, V planes). + */ +#define MAX_SUB_ALLOCS 3 + +/* Format is not YCbCr (e.g. a RGB format) - bIsYUVFormat should be false */ +#define YUV_CHROMA_ORDER_NONE 0 +/* Cb follows Y */ +#define YUV_CHROMA_ORDER_CBCR_UV 1 +/* Cr follows Y */ +#define YUV_CHROMA_ORDER_CRCB_VU 2 + +typedef struct +{ + native_handle_t base; + + /* These fields can be sent cross process. They are also valid + * to duplicate within the same process. + * + * A table is stored within psPrivateData on gralloc_module_t (this + * is obviously per-process) which maps stamps to a mapped + * PVRSRV_CLIENT_MEM_INFO in that process. Each map entry has a lock + * count associated with it, satisfying the requirements of the + * Android API. This also prevents us from leaking maps/allocations. + * + * This table has entries inserted either by alloc() + * (alloc_device_t) or map() (gralloc_module_t). Entries are removed + * by free() (alloc_device_t) and unmap() (gralloc_module_t). + * + * As a special case for framebuffer_device_t, framebuffer_open() + * will add and framebuffer_close() will remove from this table. + */ + +#define IMG_NATIVE_HANDLE_NUMFDS MAX_SUB_ALLOCS + /* The `fd' field is used to "export" a meminfo to another process. + * Therefore, it is allocated by alloc_device_t, and consumed by + * gralloc_module_t. The framebuffer_device_t does not need a handle, + * and the special value IMG_FRAMEBUFFER_FD is used instead. + */ + int fd[MAX_SUB_ALLOCS]; + +#define IMG_NATIVE_HANDLE_NUMINTS \ + ((sizeof(unsigned long long) / sizeof(int)) + 5 + MAX_SUB_ALLOCS + 1 + MAX_SUB_ALLOCS + MAX_SUB_ALLOCS) + /* A KERNEL unique identifier for any exported kernel meminfo. Each + * exported kernel meminfo will have a unique stamp, but note that in + * userspace, several meminfos across multiple processes could have + * the same stamp. As the native_handle can be dup(2)'d, there could be + * multiple handles with the same stamp but different file descriptors. + */ + unsigned long long ui64Stamp; + + /* This is used for buffer usage validation when locking a buffer, + * and also in WSEGL (for the composition bypass feature). + */ + int usage; + + /* In order to do efficient cache flushes we need the buffer dimensions + * and format. These are available on the ANativeWindowBuffer, + * but the platform doesn't pass them down to the graphics HAL. + */ + int iWidth; + int iHeight; + int iFormat; + unsigned int uiBpp; + + /* The ion allocation path doesn't allow for the allocation size and + * mapping flags to be communicated cross-process automatically. + * Cache these here so we can map buffers in client processes. + */ + unsigned int uiAllocSize[MAX_SUB_ALLOCS]; + unsigned int uiFlags; + /* For multi-planar allocations, there will be multiple hstrides */ + int aiStride[MAX_SUB_ALLOCS]; + + /* For multi-planar allocations, there will be multiple vstrides */ + int aiVStride[MAX_SUB_ALLOCS]; + +} +__attribute__((aligned(sizeof(int)),packed)) IMG_native_handle_t; + +typedef struct +{ + int l, t, w, h; +} +IMG_write_lock_rect_t; + +typedef int (*IMG_buffer_format_compute_params_pfn)( + unsigned int uiPlane, int *piWidth, int *piHeight, + int *piStride, int *piVStride, unsigned long *pulPlaneOffset); + +typedef struct IMG_buffer_format_public_t +{ + /* Buffer formats are returned as a linked list */ + struct IMG_buffer_format_public_t *psNext; + + /* HAL_PIXEL_FORMAT_... enumerant */ + int iHalPixelFormat; + + /* WSEGL_PIXELFORMAT_... enumerant */ + int iWSEGLPixelFormat; + + /* Friendly name for format */ + const char *const szName; + + /* Bits (not bytes) per pixel */ + unsigned int uiBpp; + + /* Supported HW usage bits. If this is GRALLOC_USAGE_HW_MASK, all usages + * are supported. Used for HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED. + */ + int iSupportedUsage; + + /* YUV output format */ + int bIsYUVFormat; + + /* YCBCR_ORDERING_* defined the order of the Cb/Cr values */ + int eYUVChromaOrder; + + /* Utility function for adjusting YUV per-plane parameters */ + IMG_buffer_format_compute_params_pfn pfnComputeParams; +} +IMG_buffer_format_public_t; + +typedef struct +{ + /* Base memtrack record, copied to caller */ + struct memtrack_record base; + + /* Record type, for filtering cached records */ + enum memtrack_type eType; + + /* Process ID, for filtering cached records */ + pid_t pid; +} +IMG_memtrack_record_public_t; + +typedef struct IMG_gralloc_module_public_t +{ + gralloc_module_t base; + + /* This function is deprecated and might be NULL. Do not use it. */ + int (*GetPhyAddrs)(gralloc_module_t const* module, + buffer_handle_t handle, void **ppvPhyAddr); + + /* Obtain HAL's registered format list */ + const IMG_buffer_format_public_t *(*GetBufferFormats)(void); + + int (*GetImplementationFormat) (struct IMG_gralloc_module_public_t const *psGrallocModule, int usage); + + int (*GetMemTrackRecords)(struct IMG_gralloc_module_public_t const *module, + IMG_memtrack_record_public_t **ppsRecords, + size_t *puNumRecords); + + /* Custom-blit components in lieu of overlay hardware */ + int (*Blit)(struct IMG_gralloc_module_public_t const *module, + buffer_handle_t src, + void *dest[MAX_SUB_ALLOCS], int format); + + int (*Blit2)(struct IMG_gralloc_module_public_t const *module, + buffer_handle_t src, buffer_handle_t dest, + int w, int h, int x, int y); + + int (*Blit3)(struct IMG_gralloc_module_public_t const *module, + unsigned long long ui64SrcStamp, int iSrcWidth, + int iSrcHeight, int iSrcFormat, int eSrcRotation, + buffer_handle_t dest, int eDestRotation); +} +IMG_gralloc_module_public_t; + +/** + * pixel format definitions + */ + +enum { + /* + * 0x100 = 0x1FF + * + * This range is reserved for pixel formats that are specific to the HAL + * implementation. Implementations can use any value in this range to + * communicate video pixel formats between their HAL modules. These + * formats must not have an alpha channel. Additionally, an EGLimage + * created from a gralloc buffer of one of these formats must be + * supported for use with the GL_OES_EGL_image_external OpenGL ES + * extension. + */ + + /* + * These are vendor specific pixel format, by (informal) convention + * IMGTec formats start from the top of the range, TI formats start from + * the bottom + */ + HAL_PIXEL_FORMAT_TI_NV12 = 0x100, + HAL_PIXEL_FORMAT_TI_UNUSED = 0x101, + HAL_PIXEL_FORMAT_TI_NV12_1D = 0x102, + HAL_PIXEL_FORMAT_TI_Y8 = 0x103, + HAL_PIXEL_FORMAT_TI_Y16 = 0x104, + HAL_PIXEL_FORMAT_TI_UYVY = 0x105, + HAL_PIXEL_FORMAT_BGRX_8888 = 0x1FF, + + /* generic format missing from Android list, not specific to vendor + * implementation + */ + HAL_PIXEL_FORMAT_NV12 = 0x3231564E, // FourCC for NV12 +}; + +#endif /* HAL_PUBLIC_H */ diff --git a/libhwcomposer/hwc.cpp b/libhwcomposer/hwc.cpp new file mode 100644 index 0000000..b04c548 --- /dev/null +++ b/libhwcomposer/hwc.cpp @@ -0,0 +1,724 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 <sstream> + +#include <cutils/trace.h> +#include <fcntl.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <sys/resource.h> + +#include <cutils/log.h> +#include <cutils/properties.h> +#define HWC_REMOVE_DEPRECATED_VERSIONS 1 +#include <hardware/hardware.h> +#include <hardware/hwcomposer.h> +#include <hardware_legacy/uevent.h> + +#include <kms++/kms++.h> + +#include "display.h" +#include "format.h" +#include "hwc_dev.h" + +#define LCD_DISPLAY_DEFAULT_HRES 1920 +#define LCD_DISPLAY_DEFAULT_VRES 1080 +#define LCD_DISPLAY_DEFAULT_FPS 60 + +#define LCD_DISPLAY_DEFAULT_DPI 120 +#define HDMI_DISPLAY_DEFAULT_DPI 75 + +#define HWC_PRIORITY_LOW_DISPLAY (19) + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) +#endif + +#define WIDTH(rect) ((rect).right - (rect).left) +#define HEIGHT(rect) ((rect).bottom - (rect).top) + +static bool is_valid_display(omap_hwc_device_t* hwc_dev, int disp) +{ + if (disp < 0 || disp >= MAX_DISPLAYS || !hwc_dev->displays[disp]) + return false; + + return true; +} + +struct drm_connector_type { + int type; + char name[64]; +}; + +#define CONNECTOR_TYPE_STR(type) { DRM_MODE_CONNECTOR_ ## type, #type } + +static const struct drm_connector_type connector_type_list[] = { + CONNECTOR_TYPE_STR(Unknown), + CONNECTOR_TYPE_STR(VGA), + CONNECTOR_TYPE_STR(DVII), + CONNECTOR_TYPE_STR(DVID), + CONNECTOR_TYPE_STR(DVIA), + CONNECTOR_TYPE_STR(Composite), + CONNECTOR_TYPE_STR(SVIDEO), + CONNECTOR_TYPE_STR(LVDS), + CONNECTOR_TYPE_STR(Component), + CONNECTOR_TYPE_STR(9PinDIN), + CONNECTOR_TYPE_STR(DisplayPort), + CONNECTOR_TYPE_STR(HDMIA), + CONNECTOR_TYPE_STR(HDMIB), + CONNECTOR_TYPE_STR(TV), + CONNECTOR_TYPE_STR(eDP), + CONNECTOR_TYPE_STR(VIRTUAL), + CONNECTOR_TYPE_STR(DSI), + CONNECTOR_TYPE_STR(DPI), +}; + +/* The connectors for primary and external displays can be controlled + * by the below system properties + * - ro.hwc.primary.conn + * - ro.hwc.external.conn + * If these are not set (default), `DRM_CONNECTOR_TYPE_Unknown` will be + * considered as primary display connector, and `DRM_CONNECTOR_TYPE_HDMIA` + * will be considered as secondary display. + * + * The values are <string> constants, with acceptable values being as defined + * by the DRM interface `DRM_CONNECTOR_TYPE_<string>`. + * + * If nothing is set, external is `HDMIA` connector. + */ +static int display_get_connector_type(int disp) +{ + char prop_val[PROPERTY_VALUE_MAX]; + + if (disp == HWC_DISPLAY_PRIMARY) + property_get("ro.hwc.primary.conn", prop_val, ""); + else + property_get("ro.hwc.external.conn", prop_val, "HDMIA"); + + for (size_t i = 0; i < ARRAY_SIZE(connector_type_list); i++) { + if (!strncasecmp(prop_val, connector_type_list[i].name, 64)) + return connector_type_list[i].type; + } + + return -1; +} + +static void get_connectors(omap_hwc_device_t* hwc_dev) +{ + int primary_connector_type = display_get_connector_type(HWC_DISPLAY_PRIMARY); + int external_connector_type = display_get_connector_type(HWC_DISPLAY_EXTERNAL); + + // Find primary connector + for (auto connector : hwc_dev->card->get_connectors()) { + if (primary_connector_type != -1) { + if (connector->connector_type() == (uint32_t)primary_connector_type) { + hwc_dev->primaryConector = connector; + break; + } + } else { + /* If connector type is not specified use the first + * connector that is not our HWC_DISPLAY_EXTERNAL connector + */ + if (connector->connector_type() != (uint32_t)external_connector_type) { + hwc_dev->primaryConector = connector; + break; + } + } + } + + // Find external connector + for (auto connector : hwc_dev->card->get_connectors()) { + if (connector->connector_type() == (uint32_t)external_connector_type) { + hwc_dev->externalConector = connector; + break; + } + } +} + +static int init_primary_display(omap_hwc_device_t* hwc_dev) +{ + if (hwc_dev->displays[HWC_DISPLAY_PRIMARY]) { + ALOGE("Display %d is already connected", HWC_DISPLAY_PRIMARY); + return -EBUSY; + } + + kms::Connector* connector = hwc_dev->primaryConector; + + HWCDisplay* display = new HWCDisplay(DISP_ROLE_PRIMARY); + hwc_dev->displays[HWC_DISPLAY_PRIMARY] = display; + + if (!connector) { + ALOGW("No connector found for primary display"); + ALOGW("Using dummy primary display"); + + display->is_dummy = true; + + display_config_t config; + config.xres = LCD_DISPLAY_DEFAULT_HRES; + config.yres = LCD_DISPLAY_DEFAULT_VRES; + config.fps = LCD_DISPLAY_DEFAULT_FPS; + config.xdpi = LCD_DISPLAY_DEFAULT_DPI; + config.ydpi = LCD_DISPLAY_DEFAULT_DPI; + display->configs.push_back(config); + + return 0; + } + + /* Always use default mode for now */ + kms::Videomode mode = connector->get_default_mode(); + + display_config_t config; + config.xres = mode.hdisplay; + config.yres = mode.vdisplay; + config.fps = mode.vrefresh; + config.xdpi = LCD_DISPLAY_DEFAULT_DPI; + config.ydpi = LCD_DISPLAY_DEFAULT_DPI; + display->configs.push_back(config); + + display->disp_link.card = hwc_dev->card; + display->disp_link.con = connector; + display->disp_link.crtc = connector->get_current_crtc(); + // FIXME: user resource manager + if (!display->disp_link.crtc) + display->disp_link.crtc = connector->get_possible_crtcs()[0]; + display->disp_link.mode = mode; + + display->setup_composition_pipes(); + + return 0; +} + +static int add_external_hdmi_display(omap_hwc_device_t* hwc_dev) +{ + if (hwc_dev->displays[HWC_DISPLAY_EXTERNAL]) { + ALOGE("Display %d is already connected", HWC_DISPLAY_EXTERNAL); + return 0; + } + + kms::Connector* connector = hwc_dev->externalConector; + + if (!connector) { + ALOGE("No connector for external display"); + return -1; + } + + /* wait until EDID read finishes */ + do { + connector->refresh(); + } while (connector->get_modes().size() == 0); + + // FIXME: Allow selecting other modes until HWC 1.4 support is added + kms::Videomode mode = connector->get_default_mode(); + + HWCDisplay* display = new HWCDisplay(DISP_ROLE_SECONDARY); + hwc_dev->displays[HWC_DISPLAY_EXTERNAL] = display; + + display_config_t config; + config.xres = mode.hdisplay; + config.yres = mode.vdisplay; + config.fps = mode.vrefresh; + config.xdpi = HDMI_DISPLAY_DEFAULT_DPI; + config.ydpi = HDMI_DISPLAY_DEFAULT_DPI; + display->configs.push_back(config); + + display->disp_link.card = hwc_dev->card; + display->disp_link.con = connector; + display->disp_link.crtc = connector->get_current_crtc(); + // FIXME: user resource manager + if (!display->disp_link.crtc) + display->disp_link.crtc = connector->get_possible_crtcs()[0]; + display->disp_link.mode = mode; + + display->setup_composition_pipes(); + + return 0; +} + +static void remove_external_hdmi_display(omap_hwc_device_t* hwc_dev) +{ + HWCDisplay* display = hwc_dev->displays[HWC_DISPLAY_EXTERNAL]; + if (!display) { + ALOGW("Failed to remove non-existent display %d", HWC_DISPLAY_EXTERNAL); + return; + } + + delete hwc_dev->displays[HWC_DISPLAY_EXTERNAL]; + hwc_dev->displays[HWC_DISPLAY_EXTERNAL] = NULL; +} + +static void handle_hotplug(omap_hwc_device_t* hwc_dev, bool state) +{ + if (state) { + int err = add_external_hdmi_display(hwc_dev); + if (err) { + remove_external_hdmi_display(hwc_dev); + return; + } + ALOGI("Added external display"); + } else { + remove_external_hdmi_display(hwc_dev); + ALOGI("Removed external display"); + } +} + +static int find_hdmi_connector_status(omap_hwc_device_t* hwc_dev) +{ + auto connector = hwc_dev->externalConector; + if (!connector) + return false; + + bool old_state = connector->connected(); + connector->refresh(); + bool cur_state = connector->connected(); + + if (cur_state != old_state) + ALOGI("%s event for connector %u\n", + cur_state == DRM_MODE_CONNECTED ? "Plug" : "Unplug", + connector->id()); + + return cur_state == DRM_MODE_CONNECTED; +} + +static bool check_hotplug_status(omap_hwc_device_t* hwc_dev, bool old_state) +{ + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + bool state = find_hdmi_connector_status(hwc_dev); + if (state != old_state) + handle_hotplug(hwc_dev, state); + + lock.unlock(); + + if (hwc_dev->cb_procs) { + if (hwc_dev->cb_procs->hotplug) + hwc_dev->cb_procs->hotplug(hwc_dev->cb_procs, HWC_DISPLAY_EXTERNAL, state); + if (hwc_dev->cb_procs->invalidate) + hwc_dev->cb_procs->invalidate(hwc_dev->cb_procs); + } + + return state; +} + +static void hwc_hdmi_thread(void* data) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)data; + + setpriority(PRIO_PROCESS, 0, HWC_PRIORITY_LOW_DISPLAY); + + uevent_init(); + + struct pollfd pfds[1] = { + { + pfds[0].fd = uevent_get_fd(), + pfds[0].events = POLLIN, + pfds[0].revents = 0, + } + }; + + const char* hdmi_uevent_path = "change@/devices/platform/omapdrm.0/drm/card0"; + static char uevent_desc[4096]; + memset(uevent_desc, 0, sizeof(uevent_desc)); + + /* Check outside of uevent loop in-case already plugged in */ + bool state = check_hotplug_status(hwc_dev, false); + + while (true) { + int err = poll(pfds, ARRAY_SIZE(pfds), -1); + if (err < 0) { + ALOGE("received hdmi_thread poll() error event %d", err); + break; + } + + if (pfds[0].revents & POLLIN) { + /* keep last 2 zeroes to ensure double 0 termination */ + uevent_next_event(uevent_desc, sizeof(uevent_desc) - 2); + if (strlen(hdmi_uevent_path) <= 0 || strcmp(uevent_desc, hdmi_uevent_path)) + continue; /* event not for us */ + + state = check_hotplug_status(hwc_dev, state); + } + } + + ALOGE("HDMI polling thread exiting\n"); +} + +/* + * DRM event polling thread + * We poll for DRM events in this thread. DRM events can be vblank and/or + * page-flip events both occurring on Vsync. + */ +static void hwc_drm_event_thread(void* data) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)data; + + setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY); + + struct pollfd pfds = { + pfds.fd = hwc_dev->card->fd(), + pfds.events = POLLIN, + pfds.revents = 0, + }; + + drmEventContext evctx = { + evctx.version = 2, + evctx.vblank_handler = HWCDisplay::vblank_handler, + evctx.page_flip_handler = HWCDisplay::page_flip_handler, + }; + + while (true) { + int ret = poll(&pfds, 1, 60000); + if (ret < 0) { + ALOGE("Event poll error %d", errno); + break; + } else if (ret == 0) { + ALOGI("Event poll timeout"); + continue; + } + if (pfds.revents & POLLIN) + drmHandleEvent(pfds.fd, &evctx); + } + + ALOGE("DRM event polling thread exiting\n"); +} + +static void adjust_drm_plane_to_layer(hwc_layer_1_t* layer, int zorder, drm_plane_props_t* plane) +{ + if (!layer || !plane) { + ALOGE("Bad layer or plane"); + return; + } + + /* display position */ + plane->crtc_x = layer->displayFrame.left; + plane->crtc_y = layer->displayFrame.top; + plane->crtc_w = WIDTH(layer->displayFrame); + plane->crtc_h = HEIGHT(layer->displayFrame); + + /* crop */ + plane->src_x = layer->sourceCrop.left; + plane->src_y = layer->sourceCrop.top; + plane->src_w = WIDTH(layer->sourceCrop); + plane->src_h = HEIGHT(layer->sourceCrop); + + plane->zorder = zorder; + plane->layer = layer; + + if (layer->blending == HWC_BLENDING_PREMULT) + plane->pre_mult_alpha = 1; + else + plane->pre_mult_alpha = 0; +} + +static int hwc_prepare_for_display(omap_hwc_device_t* hwc_dev, int disp, hwc_display_contents_1_t* content) +{ + if (!is_valid_display(hwc_dev, disp)) + return -ENODEV; + + HWCDisplay* display = hwc_dev->displays[disp]; + + if (display->is_dummy) { + for (size_t i = 0; i < content->numHwLayers - 1; i++) { + hwc_layer_1_t* layer = &content->hwLayers[i]; + layer->compositionType = HWC_OVERLAY; + } + return 0; + } + + /* Set the FB_TARGET layer */ + adjust_drm_plane_to_layer(&content->hwLayers[content->numHwLayers - 1], 0, &display->planeProps[disp]); + + return 0; +} + +static int hwc_prepare(struct hwc_composer_device_1* dev, size_t numDisplays, hwc_display_contents_1_t** displays) +{ + atrace_begin(ATRACE_TAG_HAL, "am57xx_hwc_prepare"); + if (!numDisplays || !displays) + return 0; + + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + int err = 0; + + for (size_t i = 0; i < numDisplays; i++) { + hwc_display_contents_1_t* contents = displays[i]; + + if (!contents) + continue; + + if (contents->numHwLayers == 0) { + ALOGW("Prepare given no content for display %d", i); + continue; + } + + int disp_err = hwc_prepare_for_display(hwc_dev, i, contents); + if (!err && disp_err) + err = disp_err; + } + + atrace_end(ATRACE_TAG_HAL); + return err; +} + +static int hwc_set_for_display(omap_hwc_device_t* hwc_dev, int disp, hwc_display_contents_1_t* content) +{ + if (!is_valid_display(hwc_dev, disp)) + return -ENODEV; + + HWCDisplay* display = hwc_dev->displays[disp]; + drm_plane_props_t* planeProp = &display->planeProps[disp]; + + int err = 0; + + /* + * clear release and retire fence fd's here in case we do + * not set them in update_display() + */ + for (size_t i = 0; i < content->numHwLayers; i++) { + hwc_layer_1_t* layer = &content->hwLayers[i]; + layer->releaseFenceFd = -1; + } + content->retireFenceFd = -1; + + if (!display->is_dummy) { + err = display->update_display(planeProp); + if (err) + ALOGE("Failed to update display %d\n", disp); + } + + /* clear any remaining acquire fences */ + for (size_t i = 0; i < content->numHwLayers; i++) { + hwc_layer_1_t* layer = &content->hwLayers[i]; + if (layer->acquireFenceFd >= 0) { + close(layer->acquireFenceFd); + layer->acquireFenceFd = -1; + } + } + + return err; +} + +static int hwc_set(struct hwc_composer_device_1* dev, size_t numDisplays, hwc_display_contents_1_t** displays) +{ + atrace_begin(ATRACE_TAG_HAL, "am57xx_hwc_set"); + if (!numDisplays || !displays) + return 0; + + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + int err = 0; + + for (size_t i = 0; i < numDisplays; i++) { + hwc_display_contents_1_t* contents = displays[i]; + + if (!contents) + continue; + + if (contents->numHwLayers == 0) { + ALOGE("Set given no content for display %d", i); + continue; + } + + int disp_err = hwc_set_for_display(hwc_dev, i, contents); + if (!err && disp_err) + err = disp_err; + } + + atrace_end(ATRACE_TAG_HAL); + return err; +} + +static int hwc_eventControl(struct hwc_composer_device_1* dev, int disp, int event, int enabled) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + if (!is_valid_display(hwc_dev, disp)) + return -EINVAL; + + switch (event) { + case HWC_EVENT_VSYNC: + // FIXME: This is a hack + hwc_dev->displays[disp]->cb_procs = hwc_dev->cb_procs; + + ALOGD("%s vsync for display %d", enabled ? "Enabling" : "Disabling", disp); + return hwc_dev->displays[disp]->set_vsync_state(enabled); + + default: + return -EINVAL; + } + + return 0; +} + +static int hwc_blank(struct hwc_composer_device_1* dev, int disp, int blank) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + ALOGD("%s display %d", blank ? "Blanking" : "Unblanking", disp); + + if (!is_valid_display(hwc_dev, disp)) + return -EINVAL; + + hwc_dev->displays[disp]->blank(blank); + + return 0; +} + +static int hwc_query(struct hwc_composer_device_1* dev, int what, int* value) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + switch (what) { + case HWC_BACKGROUND_LAYER_SUPPORTED: + // we don't support the background layer yet + value[0] = 0; + break; + case HWC_VSYNC_PERIOD: + ALOGW("Query for deprecated vsync value, returning 60Hz"); + *value = 1000 * 1000 * 1000 / 60; + break; + case HWC_DISPLAY_TYPES_SUPPORTED: + *value = HWC_DISPLAY_PRIMARY_BIT | HWC_DISPLAY_EXTERNAL_BIT; + break; + default: + // unsupported query + return -EINVAL; + } + + return 0; +} + +static void hwc_registerProcs(struct hwc_composer_device_1* dev, hwc_procs_t const* procs) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + hwc_dev->cb_procs = (typeof(hwc_dev->cb_procs))procs; + + /* now that cb_procs->hotplug is valid */ + try { + hwc_dev->hdmi_thread = new std::thread(hwc_hdmi_thread, hwc_dev); + } catch (...) { + ALOGE("Failed to create HDMI listening thread (%s)", strerror(errno)); + } +} + +static int hwc_getDisplayConfigs(struct hwc_composer_device_1* dev, int disp, uint32_t* configs, size_t* numConfigs) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + if (!is_valid_display(hwc_dev, disp)) + return -EINVAL; + + HWCDisplay* display = hwc_dev->displays[disp]; + + return display->get_display_configs(configs, numConfigs); +} + +static int hwc_getDisplayAttributes(struct hwc_composer_device_1* dev, int disp, uint32_t config, const uint32_t* attributes, int32_t* values) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)dev; + std::unique_lock<std::mutex> lock(hwc_dev->mutex); + + if (!is_valid_display(hwc_dev, disp)) + return -EINVAL; + + HWCDisplay* display = hwc_dev->displays[disp]; + + return display->get_display_attributes(config, attributes, values); +} + +static int hwc_device_close(hw_device_t* device) +{ + omap_hwc_device_t* hwc_dev = (omap_hwc_device_t*)device; + + if (hwc_dev) { + if (hwc_dev->event_thread) + delete hwc_dev->event_thread; + + for (size_t i = 0; i < MAX_DISPLAYS; i++) + delete hwc_dev->displays[i]; + + delete hwc_dev; + } + + return 0; +} + +static int hwc_device_open(const hw_module_t* module, const char* name, hw_device_t** device) +{ + if (strcmp(name, HWC_HARDWARE_COMPOSER)) + return -EINVAL; + + omap_hwc_device_t* hwc_dev = new omap_hwc_device_t; + memset(hwc_dev, 0, sizeof(*hwc_dev)); + + /* Open DRM device */ + hwc_dev->card = new kms::Card(); + + /* Find primary and external connectors */ + get_connectors(hwc_dev); + + int ret = init_primary_display(hwc_dev); + if (ret) { + ALOGE("Could not initialize primary display"); + return -1; + } + + hwc_dev->event_thread = new std::thread(hwc_drm_event_thread, hwc_dev); + + hwc_dev->device.common.tag = HARDWARE_DEVICE_TAG; + hwc_dev->device.common.version = HWC_DEVICE_API_VERSION_1_1; + hwc_dev->device.common.module = (hw_module_t*)module; + hwc_dev->device.common.close = hwc_device_close; + hwc_dev->device.prepare = hwc_prepare; + hwc_dev->device.set = hwc_set; + hwc_dev->device.eventControl = hwc_eventControl; + hwc_dev->device.blank = hwc_blank; + hwc_dev->device.query = hwc_query; + hwc_dev->device.registerProcs = hwc_registerProcs; + hwc_dev->device.getDisplayConfigs = hwc_getDisplayConfigs; + hwc_dev->device.getDisplayAttributes = hwc_getDisplayAttributes; + + *device = &hwc_dev->device.common; + + return 0; +} + +static struct hw_module_methods_t module_methods = { + .open = hwc_device_open, +}; + +omap_hwc_module_t HAL_MODULE_INFO_SYM = { + .base = { + .common = { + .tag = HARDWARE_MODULE_TAG, + .module_api_version = HWC_MODULE_API_VERSION_0_1, + .hal_api_version = HARDWARE_HAL_API_VERSION, + .id = HWC_HARDWARE_MODULE_ID, + .name = "AM57xx Hardware Composer HAL", + .author = "Texas Instruments", + .methods = &module_methods, + }, + }, +}; diff --git a/libhwcomposer/hwc_dev.h b/libhwcomposer/hwc_dev.h new file mode 100644 index 0000000..03d413b --- /dev/null +++ b/libhwcomposer/hwc_dev.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ + * + * 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 HWC_DEV_H +#define HWC_DEV_H + +#include <mutex> +#include <thread> + +#include <cstdbool> +#include <cstdint> + +#include <hardware/hwcomposer.h> +#include <kms++/kms++.h> + +#include "display.h" + +typedef struct omap_hwc_module { + hwc_module_t base; + +} omap_hwc_module_t; + +typedef struct omap_hwc_device { + /* static data */ + hwc_composer_device_1_t device; + + std::mutex mutex; + + std::thread* hdmi_thread; + std::thread* event_thread; + + kms::Card* card; + + HWCDisplay* displays[MAX_DISPLAYS]; + + kms::Connector* primaryConector; + kms::Connector* externalConector; + + drmEventContext evctx; + + const hwc_procs_t* cb_procs; +} omap_hwc_device_t; + +#endif /* HWC_DEV_H */ |