diff options
Diffstat (limited to 'drm')
30 files changed, 2052 insertions, 1671 deletions
diff --git a/drm/DrmAtomicStateManager.cpp b/drm/DrmAtomicStateManager.cpp new file mode 100644 index 0000000..65fb19e --- /dev/null +++ b/drm/DrmAtomicStateManager.cpp @@ -0,0 +1,191 @@ +/* + * Copyright (C) 2015 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. + */ + +#define ATRACE_TAG ATRACE_TAG_GRAPHICS +#define LOG_TAG "hwc-drm-atomic-state-manager" + +#include "DrmAtomicStateManager.h" + +#include <drm/drm_mode.h> +#include <pthread.h> +#include <sched.h> +#include <sync/sync.h> +#include <utils/Trace.h> + +#include <array> +#include <cstdlib> +#include <ctime> +#include <sstream> +#include <vector> + +#include "drm/DrmCrtc.h" +#include "drm/DrmDevice.h" +#include "drm/DrmPlane.h" +#include "drm/DrmUnique.h" +#include "utils/log.h" + +namespace android { + +// NOLINTNEXTLINE (readability-function-cognitive-complexity): Fixme +auto DrmAtomicStateManager::CommitFrame(AtomicCommitArgs &args) -> int { + ATRACE_CALL(); + + if (args.active && *args.active == active_frame_state_.crtc_active_state) { + /* Don't set the same state twice */ + args.active.reset(); + } + + if (!args.HasInputs()) { + /* nothing to do */ + return 0; + } + + if (!active_frame_state_.crtc_active_state) { + /* Force activate display */ + args.active = true; + } + + auto new_frame_state = NewFrameState(); + + auto *drm = pipe_->device; + auto *connector = pipe_->connector->Get(); + auto *crtc = pipe_->crtc->Get(); + + auto pset = MakeDrmModeAtomicReqUnique(); + if (!pset) { + ALOGE("Failed to allocate property set"); + return -ENOMEM; + } + + int64_t out_fence = -1; + if (crtc->GetOutFencePtrProperty() && + !crtc->GetOutFencePtrProperty().AtomicSet(*pset, uint64_t(&out_fence))) { + return -EINVAL; + } + + if (args.active) { + new_frame_state.crtc_active_state = *args.active; + if (!crtc->GetActiveProperty().AtomicSet(*pset, *args.active ? 1 : 0) || + !connector->GetCrtcIdProperty().AtomicSet(*pset, crtc->GetId())) { + return -EINVAL; + } + } + + if (args.display_mode) { + new_frame_state.mode_blob = args.display_mode.value().CreateModeBlob(*drm); + + if (!new_frame_state.mode_blob) { + ALOGE("Failed to create mode_blob"); + return -EINVAL; + } + + if (!crtc->GetModeProperty().AtomicSet(*pset, *new_frame_state.mode_blob)) { + return -EINVAL; + } + } + + auto unused_planes = new_frame_state.used_planes; + + if (args.composition) { + new_frame_state.used_framebuffers.clear(); + new_frame_state.used_planes.clear(); + + for (auto &joining : args.composition->plan) { + DrmPlane *plane = joining.plane->Get(); + DrmHwcLayer &layer = joining.layer; + + new_frame_state.used_framebuffers.emplace_back(layer.fb_id_handle); + new_frame_state.used_planes.emplace_back(joining.plane); + + /* Remove from 'unused' list, since plane is re-used */ + auto &v = unused_planes; + v.erase(std::remove(v.begin(), v.end(), joining.plane), v.end()); + + if (plane->AtomicSetState(*pset, layer, joining.z_pos, crtc->GetId()) != + 0) { + return -EINVAL; + } + } + } + + if (args.composition) { + for (auto &plane : unused_planes) { + if (plane->Get()->AtomicDisablePlane(*pset) != 0) { + return -EINVAL; + } + } + } + + uint32_t flags = DRM_MODE_ATOMIC_ALLOW_MODESET; + if (args.test_only) + flags |= DRM_MODE_ATOMIC_TEST_ONLY; + + int err = drmModeAtomicCommit(drm->GetFd(), pset.get(), flags, drm); + if (err != 0) { + if (!args.test_only) + ALOGE("Failed to commit pset ret=%d\n", err); + return err; + } + + if (!args.test_only) { + if (args.display_mode) { + /* TODO(nobody): we still need this for synthetic vsync, remove after + * vsync reworked */ + connector->SetActiveMode(*args.display_mode); + } + + active_frame_state_ = std::move(new_frame_state); + + if (crtc->GetOutFencePtrProperty()) { + args.out_fence = UniqueFd((int)out_fence); + } + } + + return 0; +} + +auto DrmAtomicStateManager::ExecuteAtomicCommit(AtomicCommitArgs &args) -> int { + int err = CommitFrame(args); + + if (!args.test_only) { + if (err != 0) { + ALOGE("Composite failed for pipeline %s", + pipe_->connector->Get()->GetName().c_str()); + // Disable the hw used by the last active composition. This allows us to + // signal the release fences from that composition to avoid hanging. + AtomicCommitArgs cl_args{}; + cl_args.composition = std::make_shared<DrmKmsPlan>(); + if (CommitFrame(cl_args) != 0) { + ALOGE("Failed to clean-up active composition for pipeline %s", + pipe_->connector->Get()->GetName().c_str()); + } + return err; + } + } + + return err; +} // namespace android + +auto DrmAtomicStateManager::ActivateDisplayUsingDPMS() -> int { + return drmModeConnectorSetProperty(pipe_->device->GetFd(), + pipe_->connector->Get()->GetId(), + pipe_->connector->Get() + ->GetDpmsProperty() + .id(), + DRM_MODE_DPMS_ON); +} + +} // namespace android diff --git a/drm/DrmAtomicStateManager.h b/drm/DrmAtomicStateManager.h new file mode 100644 index 0000000..08a1c13 --- /dev/null +++ b/drm/DrmAtomicStateManager.h @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2015 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 ANDROID_DRM_ATOMIC_STATE_MANAGER_H_ +#define ANDROID_DRM_ATOMIC_STATE_MANAGER_H_ + +#include <pthread.h> + +#include <functional> +#include <memory> +#include <optional> +#include <sstream> +#include <tuple> + +#include "compositor/DrmKmsPlan.h" +#include "drm/DrmPlane.h" +#include "drm/ResourceManager.h" +#include "drm/VSyncWorker.h" +#include "drmhwcomposer.h" + +namespace android { + +struct AtomicCommitArgs { + /* inputs. All fields are optional, but at least one has to be specified */ + bool test_only = false; + std::optional<DrmMode> display_mode; + std::optional<bool> active; + std::shared_ptr<DrmKmsPlan> composition; + + /* out */ + UniqueFd out_fence; + + /* helpers */ + auto HasInputs() -> bool { + return display_mode || active || composition; + } +}; + +class DrmAtomicStateManager { + public: + explicit DrmAtomicStateManager(DrmDisplayPipeline *pipe) : pipe_(pipe){}; + DrmAtomicStateManager(const DrmAtomicStateManager &) = delete; + ~DrmAtomicStateManager() = default; + + auto ExecuteAtomicCommit(AtomicCommitArgs &args) -> int; + auto ActivateDisplayUsingDPMS() -> int; + + private: + auto CommitFrame(AtomicCommitArgs &args) -> int; + + struct KmsState { + /* Required to cleanup unused planes */ + std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> used_planes; + /* We have to hold a reference to framebuffer while displaying it , + * otherwise picture will blink */ + std::vector<std::shared_ptr<DrmFbIdHandle>> used_framebuffers; + + DrmModeUserPropertyBlobUnique mode_blob; + + /* To avoid setting the inactive state twice, which will fail the commit */ + bool crtc_active_state{}; + } active_frame_state_; + + auto NewFrameState() -> KmsState { + return (KmsState){ + .used_planes = active_frame_state_.used_planes, + .used_framebuffers = active_frame_state_.used_framebuffers, + .crtc_active_state = active_frame_state_.crtc_active_state, + }; + } + + DrmDisplayPipeline *const pipe_; +}; +} // namespace android + +#endif // ANDROID_DRM_DISPLAY_COMPOSITOR_H_ diff --git a/drm/DrmConnector.cpp b/drm/DrmConnector.cpp index f1b6c1b..4737316 100644 --- a/drm/DrmConnector.cpp +++ b/drm/DrmConnector.cpp @@ -18,237 +18,174 @@ #include "DrmConnector.h" -#include <errno.h> -#include <log/log.h> -#include <stdint.h> #include <xf86drmMode.h> #include <array> +#include <cerrno> +#include <cstdint> #include <sstream> #include "DrmDevice.h" +#include "utils/log.h" + +#ifndef DRM_MODE_CONNECTOR_SPI +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DRM_MODE_CONNECTOR_SPI 19 +#endif + +#ifndef DRM_MODE_CONNECTOR_USB +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define DRM_MODE_CONNECTOR_USB 20 +#endif namespace android { -constexpr size_t TYPES_COUNT = 17; - -DrmConnector::DrmConnector(DrmDevice *drm, drmModeConnectorPtr c, - DrmEncoder *current_encoder, - std::vector<DrmEncoder *> &possible_encoders) - : drm_(drm), - id_(c->connector_id), - encoder_(current_encoder), - display_(-1), - type_(c->connector_type), - type_id_(c->connector_type_id), - state_(c->connection), - mm_width_(c->mmWidth), - mm_height_(c->mmHeight), - possible_encoders_(possible_encoders) { +constexpr size_t kTypesCount = 21; + +static bool GetOptionalConnectorProperty(const DrmDevice &dev, + const DrmConnector &connector, + const char *prop_name, + DrmProperty *property) { + return dev.GetProperty(connector.GetId(), DRM_MODE_OBJECT_CONNECTOR, + prop_name, property) == 0; } -int DrmConnector::Init() { - int ret = drm_->GetConnectorProperty(*this, "DPMS", &dpms_property_); - if (ret) { - ALOGE("Could not get DPMS property\n"); - return ret; - } - ret = drm_->GetConnectorProperty(*this, "CRTC_ID", &crtc_id_property_); - if (ret) { - ALOGE("Could not get CRTC_ID property\n"); - return ret; +static bool GetConnectorProperty(const DrmDevice &dev, + const DrmConnector &connector, + const char *prop_name, DrmProperty *property) { + if (!GetOptionalConnectorProperty(dev, connector, prop_name, property)) { + ALOGE("Could not get %s property\n", prop_name); + return false; } - ret = UpdateEdidProperty(); - if (writeback()) { - ret = drm_->GetConnectorProperty(*this, "WRITEBACK_PIXEL_FORMATS", - &writeback_pixel_formats_); - if (ret) { - ALOGE("Could not get WRITEBACK_PIXEL_FORMATS connector_id = %d\n", id_); - return ret; - } - ret = drm_->GetConnectorProperty(*this, "WRITEBACK_FB_ID", - &writeback_fb_id_); - if (ret) { - ALOGE("Could not get WRITEBACK_FB_ID connector_id = %d\n", id_); - return ret; - } - ret = drm_->GetConnectorProperty(*this, "WRITEBACK_OUT_FENCE_PTR", - &writeback_out_fence_); - if (ret) { - ALOGE("Could not get WRITEBACK_OUT_FENCE_PTR connector_id = %d\n", id_); - return ret; - } - } - return 0; + return true; } -int DrmConnector::UpdateEdidProperty() { - int ret = drm_->GetConnectorProperty(*this, "EDID", &edid_property_); - if (ret) { - ALOGW("Could not get EDID property\n"); +auto DrmConnector::CreateInstance(DrmDevice &dev, uint32_t connector_id, + uint32_t index) + -> std::unique_ptr<DrmConnector> { + auto conn = MakeDrmModeConnectorUnique(dev.GetFd(), connector_id); + if (!conn) { + ALOGE("Failed to get connector %d", connector_id); + return {}; } - return ret; -} -int DrmConnector::GetEdidBlob(drmModePropertyBlobPtr &blob) { - uint64_t blob_id; - int ret = UpdateEdidProperty(); - if (ret) { - return ret; + auto c = std::unique_ptr<DrmConnector>( + new DrmConnector(std::move(conn), &dev, index)); + + if (!GetConnectorProperty(dev, *c, "DPMS", &c->dpms_property_) || + !GetConnectorProperty(dev, *c, "CRTC_ID", &c->crtc_id_property_)) { + return {}; } - std::tie(ret, blob_id) = edid_property().value(); - if (ret) { - return ret; + c->UpdateEdidProperty(); + + if (c->IsWriteback() && + (!GetConnectorProperty(dev, *c, "WRITEBACK_PIXEL_FORMATS", + &c->writeback_pixel_formats_) || + !GetConnectorProperty(dev, *c, "WRITEBACK_FB_ID", + &c->writeback_fb_id_) || + !GetConnectorProperty(dev, *c, "WRITEBACK_OUT_FENCE_PTR", + &c->writeback_out_fence_))) { + return {}; } - blob = drmModeGetPropertyBlob(drm_->fd(), blob_id); - return !blob; + return c; } -uint32_t DrmConnector::id() const { - return id_; +int DrmConnector::UpdateEdidProperty() { + return GetOptionalConnectorProperty(*drm_, *this, "EDID", &edid_property_) + ? 0 + : -EINVAL; } -int DrmConnector::display() const { - return display_; -} +auto DrmConnector::GetEdidBlob() -> DrmModePropertyBlobUnique { + uint64_t blob_id = 0; + int ret = UpdateEdidProperty(); + if (ret != 0) { + return {}; + } -void DrmConnector::set_display(int display) { - display_ = display; + std::tie(ret, blob_id) = GetEdidProperty().value(); + if (ret != 0) { + return {}; + } + + return MakeDrmModePropertyBlobUnique(drm_->GetFd(), blob_id); } -bool DrmConnector::internal() const { - return type_ == DRM_MODE_CONNECTOR_LVDS || type_ == DRM_MODE_CONNECTOR_eDP || - type_ == DRM_MODE_CONNECTOR_DSI || - type_ == DRM_MODE_CONNECTOR_VIRTUAL || type_ == DRM_MODE_CONNECTOR_DPI; +bool DrmConnector::IsInternal() const { + auto type = connector_->connector_type; + return type == DRM_MODE_CONNECTOR_LVDS || type == DRM_MODE_CONNECTOR_eDP || + type == DRM_MODE_CONNECTOR_DSI || type == DRM_MODE_CONNECTOR_VIRTUAL || + type == DRM_MODE_CONNECTOR_DPI || type == DRM_MODE_CONNECTOR_SPI; } -bool DrmConnector::external() const { - return type_ == DRM_MODE_CONNECTOR_HDMIA || - type_ == DRM_MODE_CONNECTOR_DisplayPort || - type_ == DRM_MODE_CONNECTOR_DVID || type_ == DRM_MODE_CONNECTOR_DVII || - type_ == DRM_MODE_CONNECTOR_VGA; +bool DrmConnector::IsExternal() const { + auto type = connector_->connector_type; + return type == DRM_MODE_CONNECTOR_HDMIA || + type == DRM_MODE_CONNECTOR_DisplayPort || + type == DRM_MODE_CONNECTOR_DVID || type == DRM_MODE_CONNECTOR_DVII || + type == DRM_MODE_CONNECTOR_VGA || type == DRM_MODE_CONNECTOR_USB; } -bool DrmConnector::writeback() const { +bool DrmConnector::IsWriteback() const { #ifdef DRM_MODE_CONNECTOR_WRITEBACK - return type_ == DRM_MODE_CONNECTOR_WRITEBACK; + return connector_->connector_type == DRM_MODE_CONNECTOR_WRITEBACK; #else return false; #endif } -bool DrmConnector::valid_type() const { - return internal() || external() || writeback(); +bool DrmConnector::IsValid() const { + return IsInternal() || IsExternal() || IsWriteback(); } -std::string DrmConnector::name() const { - constexpr std::array<const char *, TYPES_COUNT> names = - {"None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", - "SVIDEO", "LVDS", "Component", "DIN", "DP", "HDMI-A", - "HDMI-B", "TV", "eDP", "Virtual", "DSI"}; +std::string DrmConnector::GetName() const { + constexpr std::array<const char *, kTypesCount> kNames = + {"None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", + "SVIDEO", "LVDS", "Component", "DIN", "DP", "HDMI-A", + "HDMI-B", "TV", "eDP", "Virtual", "DSI", "DPI", + "Writeback", "SPI", "USB"}; - if (type_ < TYPES_COUNT) { + if (connector_->connector_type < kTypesCount) { std::ostringstream name_buf; - name_buf << names[type_] << "-" << type_id_; + name_buf << kNames[connector_->connector_type] << "-" + << connector_->connector_type_id; return name_buf.str(); - } else { - ALOGE("Unknown type in connector %d, could not make his name", id_); - return "None"; } + + ALOGE("Unknown type in connector %d, could not make his name", GetId()); + return "None"; } int DrmConnector::UpdateModes() { - int fd = drm_->fd(); - - drmModeConnectorPtr c = drmModeGetConnector(fd, id_); - if (!c) { - ALOGE("Failed to get connector %d", id_); + auto conn = MakeDrmModeConnectorUnique(drm_->GetFd(), GetId()); + if (!conn) { + ALOGE("Failed to get connector %d", GetId()); return -ENODEV; } + connector_ = std::move(conn); - state_ = c->connection; - - bool preferred_mode_found = false; - std::vector<DrmMode> new_modes; - for (int i = 0; i < c->count_modes; ++i) { + modes_.clear(); + for (int i = 0; i < connector_->count_modes; ++i) { bool exists = false; for (const DrmMode &mode : modes_) { - if (mode == c->modes[i]) { - new_modes.push_back(mode); + if (mode == connector_->modes[i]) { exists = true; break; } } + if (!exists) { - DrmMode m(&c->modes[i]); - m.set_id(drm_->next_mode_id()); - new_modes.push_back(m); + modes_.emplace_back(DrmMode(&connector_->modes[i])); } - // Use only the first DRM_MODE_TYPE_PREFERRED mode found - if (!preferred_mode_found && - (new_modes.back().type() & DRM_MODE_TYPE_PREFERRED)) { - preferred_mode_id_ = new_modes.back().id(); - preferred_mode_found = true; - } - } - modes_.swap(new_modes); - if (!preferred_mode_found && modes_.size() != 0) { - preferred_mode_id_ = modes_[0].id(); } - return 0; -} -const DrmMode &DrmConnector::active_mode() const { - return active_mode_; + return 0; } -void DrmConnector::set_active_mode(const DrmMode &mode) { +void DrmConnector::SetActiveMode(DrmMode &mode) { active_mode_ = mode; } -const DrmProperty &DrmConnector::dpms_property() const { - return dpms_property_; -} - -const DrmProperty &DrmConnector::crtc_id_property() const { - return crtc_id_property_; -} - -const DrmProperty &DrmConnector::edid_property() const { - return edid_property_; -} - -const DrmProperty &DrmConnector::writeback_pixel_formats() const { - return writeback_pixel_formats_; -} - -const DrmProperty &DrmConnector::writeback_fb_id() const { - return writeback_fb_id_; -} - -const DrmProperty &DrmConnector::writeback_out_fence() const { - return writeback_out_fence_; -} - -DrmEncoder *DrmConnector::encoder() const { - return encoder_; -} - -void DrmConnector::set_encoder(DrmEncoder *encoder) { - encoder_ = encoder; -} - -drmModeConnection DrmConnector::state() const { - return state_; -} - -uint32_t DrmConnector::mm_width() const { - return mm_width_; -} - -uint32_t DrmConnector::mm_height() const { - return mm_height_; -} } // namespace android diff --git a/drm/DrmConnector.h b/drm/DrmConnector.h index 8533af8..629b3cc 100644 --- a/drm/DrmConnector.h +++ b/drm/DrmConnector.h @@ -17,87 +17,112 @@ #ifndef ANDROID_DRM_CONNECTOR_H_ #define ANDROID_DRM_CONNECTOR_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> #include <string> #include <vector> #include "DrmEncoder.h" #include "DrmMode.h" #include "DrmProperty.h" +#include "DrmUnique.h" namespace android { class DrmDevice; -class DrmConnector { +class DrmConnector : public PipelineBindable<DrmConnector> { public: - DrmConnector(DrmDevice *drm, drmModeConnectorPtr c, - DrmEncoder *current_encoder, - std::vector<DrmEncoder *> &possible_encoders); + static auto CreateInstance(DrmDevice &dev, uint32_t connector_id, + uint32_t index) -> std::unique_ptr<DrmConnector>; + DrmConnector(const DrmProperty &) = delete; DrmConnector &operator=(const DrmProperty &) = delete; - int Init(); int UpdateEdidProperty(); - int GetEdidBlob(drmModePropertyBlobPtr &blob); + auto GetEdidBlob() -> DrmModePropertyBlobUnique; + + auto GetDev() const -> DrmDevice & { + return *drm_; + } - uint32_t id() const; + auto GetId() const { + return connector_->connector_id; + } - int display() const; - void set_display(int display); + auto GetIndexInResArray() const { + return index_in_res_array_; + } - bool internal() const; - bool external() const; - bool writeback() const; - bool valid_type() const; + auto GetCurrentEncoderId() const { + return connector_->encoder_id; + } - std::string name() const; + auto SupportsEncoder(DrmEncoder &enc) const { + for (int i = 0; i < connector_->count_encoders; i++) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + if (connector_->encoders[i] == enc.GetId()) { + return true; + } + } + + return false; + } + + bool IsInternal() const; + bool IsExternal() const; + bool IsWriteback() const; + bool IsValid() const; + + std::string GetName() const; int UpdateModes(); - const std::vector<DrmMode> &modes() const { + auto &GetModes() const { return modes_; } - const DrmMode &active_mode() const; - void set_active_mode(const DrmMode &mode); - - const DrmProperty &dpms_property() const; - const DrmProperty &crtc_id_property() const; - const DrmProperty &edid_property() const; - const DrmProperty &writeback_pixel_formats() const; - const DrmProperty &writeback_fb_id() const; - const DrmProperty &writeback_out_fence() const; - - const std::vector<DrmEncoder *> &possible_encoders() const { - return possible_encoders_; + + auto &GetActiveMode() const { + return active_mode_; + } + + void SetActiveMode(DrmMode &mode); + + auto &GetDpmsProperty() const { + return dpms_property_; } - DrmEncoder *encoder() const; - void set_encoder(DrmEncoder *encoder); - drmModeConnection state() const; + auto &GetCrtcIdProperty() const { + return crtc_id_property_; + } - uint32_t mm_width() const; - uint32_t mm_height() const; + auto &GetEdidProperty() const { + return edid_property_; + } - uint32_t get_preferred_mode_id() const { - return preferred_mode_id_; + auto IsConnected() const { + return connector_->connection == DRM_MODE_CONNECTED; } - private: - DrmDevice *drm_; + auto GetMmWidth() const { + return connector_->mmWidth; + } + + auto GetMmHeight() const { + return connector_->mmHeight; + }; - uint32_t id_; - DrmEncoder *encoder_; - int display_; + private: + DrmConnector(DrmModeConnectorUnique connector, DrmDevice *drm, uint32_t index) + : connector_(std::move(connector)), + drm_(drm), + index_in_res_array_(index){}; - uint32_t type_; - uint32_t type_id_; - drmModeConnection state_; + DrmModeConnectorUnique connector_; + DrmDevice *const drm_; - uint32_t mm_width_; - uint32_t mm_height_; + const uint32_t index_in_res_array_; DrmMode active_mode_; std::vector<DrmMode> modes_; @@ -108,10 +133,6 @@ class DrmConnector { DrmProperty writeback_pixel_formats_; DrmProperty writeback_fb_id_; DrmProperty writeback_out_fence_; - - std::vector<DrmEncoder *> possible_encoders_; - - uint32_t preferred_mode_id_; }; } // namespace android diff --git a/drm/DrmCrtc.cpp b/drm/DrmCrtc.cpp index 4ce8cfc..b54f14b 100644 --- a/drm/DrmCrtc.cpp +++ b/drm/DrmCrtc.cpp @@ -18,68 +18,50 @@ #include "DrmCrtc.h" -#include <log/log.h> -#include <stdint.h> +#include <utils/log.h> #include <xf86drmMode.h> +#include <cstdint> + #include "DrmDevice.h" namespace android { -DrmCrtc::DrmCrtc(DrmDevice *drm, drmModeCrtcPtr c, unsigned pipe) - : drm_(drm), id_(c->crtc_id), pipe_(pipe), display_(-1), mode_(&c->mode) { +static int GetCrtcProperty(const DrmDevice &dev, const DrmCrtc &crtc, + const char *prop_name, DrmProperty *property) { + return dev.GetProperty(crtc.GetId(), DRM_MODE_OBJECT_CRTC, prop_name, + property); } -int DrmCrtc::Init() { - int ret = drm_->GetCrtcProperty(*this, "ACTIVE", &active_property_); - if (ret) { +auto DrmCrtc::CreateInstance(DrmDevice &dev, uint32_t crtc_id, uint32_t index) + -> std::unique_ptr<DrmCrtc> { + auto crtc = MakeDrmModeCrtcUnique(dev.GetFd(), crtc_id); + if (!crtc) { + ALOGE("Failed to get CRTC %d", crtc_id); + return {}; + } + + auto c = std::unique_ptr<DrmCrtc>(new DrmCrtc(std::move(crtc), index)); + + int ret = GetCrtcProperty(dev, *c, "ACTIVE", &c->active_property_); + if (ret != 0) { ALOGE("Failed to get ACTIVE property"); - return ret; + return {}; } - ret = drm_->GetCrtcProperty(*this, "MODE_ID", &mode_property_); - if (ret) { + ret = GetCrtcProperty(dev, *c, "MODE_ID", &c->mode_property_); + if (ret != 0) { ALOGE("Failed to get MODE_ID property"); - return ret; + return {}; } - ret = drm_->GetCrtcProperty(*this, "OUT_FENCE_PTR", &out_fence_ptr_property_); - if (ret) { + ret = GetCrtcProperty(dev, *c, "OUT_FENCE_PTR", &c->out_fence_ptr_property_); + if (ret != 0) { ALOGE("Failed to get OUT_FENCE_PTR property"); - return ret; + return {}; } - return 0; -} - -uint32_t DrmCrtc::id() const { - return id_; -} - -unsigned DrmCrtc::pipe() const { - return pipe_; -} -int DrmCrtc::display() const { - return display_; + return c; } -void DrmCrtc::set_display(int display) { - display_ = display; -} - -bool DrmCrtc::can_bind(int display) const { - return display_ == -1 || display_ == display; -} - -const DrmProperty &DrmCrtc::active_property() const { - return active_property_; -} - -const DrmProperty &DrmCrtc::mode_property() const { - return mode_property_; -} - -const DrmProperty &DrmCrtc::out_fence_ptr_property() const { - return out_fence_ptr_property_; -} } // namespace android diff --git a/drm/DrmCrtc.h b/drm/DrmCrtc.h index 7972bef..ebf0a97 100644 --- a/drm/DrmCrtc.h +++ b/drm/DrmCrtc.h @@ -17,44 +17,55 @@ #ifndef ANDROID_DRM_CRTC_H_ #define ANDROID_DRM_CRTC_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> + +#include "DrmDisplayPipeline.h" #include "DrmMode.h" #include "DrmProperty.h" +#include "DrmUnique.h" namespace android { class DrmDevice; -class DrmCrtc { +class DrmCrtc : public PipelineBindable<DrmCrtc> { public: - DrmCrtc(DrmDevice *drm, drmModeCrtcPtr c, unsigned pipe); + static auto CreateInstance(DrmDevice &dev, uint32_t crtc_id, uint32_t index) + -> std::unique_ptr<DrmCrtc>; + + DrmCrtc() = delete; DrmCrtc(const DrmCrtc &) = delete; DrmCrtc &operator=(const DrmCrtc &) = delete; - int Init(); + auto GetId() const { + return crtc_->crtc_id; + } - uint32_t id() const; - unsigned pipe() const; + auto GetIndexInResArray() const { + return index_in_res_array_; + } - int display() const; - void set_display(int display); + auto &GetActiveProperty() const { + return active_property_; + } - bool can_bind(int display) const; + auto &GetModeProperty() const { + return mode_property_; + } - const DrmProperty &active_property() const; - const DrmProperty &mode_property() const; - const DrmProperty &out_fence_ptr_property() const; + auto &GetOutFencePtrProperty() const { + return out_fence_ptr_property_; + } private: - DrmDevice *drm_; + DrmCrtc(DrmModeCrtcUnique crtc, uint32_t index) + : crtc_(std::move(crtc)), index_in_res_array_(index){}; - uint32_t id_; - unsigned pipe_; - int display_; + DrmModeCrtcUnique crtc_; - DrmMode mode_; + const uint32_t index_in_res_array_; DrmProperty active_property_; DrmProperty mode_property_; diff --git a/drm/DrmDevice.cpp b/drm/DrmDevice.cpp index bf1a5e2..fd4589e 100644 --- a/drm/DrmDevice.cpp +++ b/drm/DrmDevice.cpp @@ -18,137 +18,70 @@ #include "DrmDevice.h" -#include <cutils/properties.h> -#include <errno.h> #include <fcntl.h> -#include <log/log.h> -#include <stdint.h> #include <xf86drm.h> #include <xf86drmMode.h> -#include <algorithm> -#include <array> #include <cinttypes> -#include <sstream> +#include <cstdint> #include <string> -static void trim_left(std::string &str) { - str.erase(std::begin(str), - std::find_if(std::begin(str), std::end(str), - [](int ch) { return !std::isspace(ch); })); -} - -static void trim_right(std::string &str) { - str.erase(std::find_if(std::rbegin(str), std::rend(str), - [](int ch) { return !std::isspace(ch); }) - .base(), - std::end(str)); -} - -static void trim(std::string &str) { - trim_left(str); - trim_right(str); -} +#include "drm/DrmAtomicStateManager.h" +#include "drm/DrmPlane.h" +#include "utils/log.h" +#include "utils/properties.h" namespace android { -static std::vector<std::string> read_primary_display_order_prop() { - std::array<char, PROPERTY_VALUE_MAX> display_order_buf; - property_get("vendor.hwc.drm.primary_display_order", display_order_buf.data(), - "..."); - - std::vector<std::string> display_order; - std::istringstream str(display_order_buf.data()); - for (std::string conn_name = ""; std::getline(str, conn_name, ',');) { - trim(conn_name); - display_order.push_back(std::move(conn_name)); - } - return display_order; -} - -static std::vector<DrmConnector *> make_primary_display_candidates( - std::vector<std::unique_ptr<DrmConnector>> &connectors) { - std::vector<DrmConnector *> primary_candidates; - std::transform(std::begin(connectors), std::end(connectors), - std::back_inserter(primary_candidates), - [](std::unique_ptr<DrmConnector> &conn) { - return conn.get(); - }); - primary_candidates.erase(std::remove_if(std::begin(primary_candidates), - std::end(primary_candidates), - [](const DrmConnector *conn) { - return conn->state() != - DRM_MODE_CONNECTED; - }), - std::end(primary_candidates)); - - std::vector<std::string> display_order = read_primary_display_order_prop(); - bool use_other = display_order.back() == "..."; - - // putting connectors from primary_display_order first - auto curr_connector = std::begin(primary_candidates); - for (const std::string &display_name : display_order) { - auto it = std::find_if(std::begin(primary_candidates), - std::end(primary_candidates), - [&display_name](const DrmConnector *conn) { - return conn->name() == display_name; - }); - if (it != std::end(primary_candidates)) { - std::iter_swap(it, curr_connector); - ++curr_connector; - } - } - - if (use_other) { - // then putting internal connectors second, everything else afterwards - std::partition(curr_connector, std::end(primary_candidates), - [](const DrmConnector *conn) { return conn->internal(); }); - } else { - primary_candidates.erase(curr_connector, std::end(primary_candidates)); - } - - return primary_candidates; -} - -DrmDevice::DrmDevice() : event_listener_(this) { -} - -DrmDevice::~DrmDevice() { - event_listener_.Exit(); +DrmDevice::DrmDevice() { + drm_fb_importer_ = std::make_unique<DrmFbImporter>(*this); } -std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) { +auto DrmDevice::Init(const char *path) -> int { /* TODO: Use drmOpenControl here instead */ - fd_.Set(open(path, O_RDWR)); - if (fd() < 0) { + fd_ = UniqueFd(open(path, O_RDWR | O_CLOEXEC)); + if (!fd_) { + // NOLINTNEXTLINE(concurrency-mt-unsafe): Fixme ALOGE("Failed to open dri %s: %s", path, strerror(errno)); - return std::make_tuple(-ENODEV, 0); + return -ENODEV; } - int ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); - if (ret) { + int ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); + if (ret != 0) { ALOGE("Failed to set universal plane cap %d", ret); - return std::make_tuple(ret, 0); + return ret; } - ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_ATOMIC, 1); - if (ret) { + ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_ATOMIC, 1); + if (ret != 0) { ALOGE("Failed to set atomic cap %d", ret); - return std::make_tuple(ret, 0); + return ret; } #ifdef DRM_CLIENT_CAP_WRITEBACK_CONNECTORS - ret = drmSetClientCap(fd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); - if (ret) { + ret = drmSetClientCap(GetFd(), DRM_CLIENT_CAP_WRITEBACK_CONNECTORS, 1); + if (ret != 0) { ALOGI("Failed to set writeback cap %d", ret); - ret = 0; } #endif - drmModeResPtr res = drmModeGetResources(fd()); + uint64_t cap_value = 0; + if (drmGetCap(GetFd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value) != 0) { + ALOGW("drmGetCap failed. Fallback to no modifier support."); + cap_value = 0; + } + HasAddFb2ModifiersSupport_ = cap_value != 0; + + drmSetMaster(GetFd()); + if (drmIsMaster(GetFd()) == 0) { + ALOGE("DRM/KMS master access required"); + return -EACCES; + } + + auto res = MakeDrmModeResUnique(GetFd()); if (!res) { ALOGE("Failed to get DrmDevice resources"); - return std::make_tuple(-ENODEV, 0); + return -ENODEV; } min_resolution_ = std::pair<uint32_t, uint32_t>(res->min_width, @@ -156,390 +89,100 @@ std::tuple<int, int> DrmDevice::Init(const char *path, int num_displays) { max_resolution_ = std::pair<uint32_t, uint32_t>(res->max_width, res->max_height); - // Assumes that the primary display will always be in the first - // drm_device opened. - bool found_primary = num_displays != 0; - - for (int i = 0; !ret && i < res->count_crtcs; ++i) { - drmModeCrtcPtr c = drmModeGetCrtc(fd(), res->crtcs[i]); - if (!c) { - ALOGE("Failed to get crtc %d", res->crtcs[i]); - ret = -ENODEV; - break; + for (int i = 0; i < res->count_crtcs; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto crtc = DrmCrtc::CreateInstance(*this, res->crtcs[i], i); + if (crtc) { + crtcs_.emplace_back(std::move(crtc)); } - - std::unique_ptr<DrmCrtc> crtc(new DrmCrtc(this, c, i)); - drmModeFreeCrtc(c); - - ret = crtc->Init(); - if (ret) { - ALOGE("Failed to initialize crtc %d", res->crtcs[i]); - break; - } - crtcs_.emplace_back(std::move(crtc)); } - std::vector<int> possible_clones; - for (int i = 0; !ret && i < res->count_encoders; ++i) { - drmModeEncoderPtr e = drmModeGetEncoder(fd(), res->encoders[i]); - if (!e) { - ALOGE("Failed to get encoder %d", res->encoders[i]); - ret = -ENODEV; - break; - } - - std::vector<DrmCrtc *> possible_crtcs; - DrmCrtc *current_crtc = NULL; - for (auto &crtc : crtcs_) { - if ((1 << crtc->pipe()) & e->possible_crtcs) - possible_crtcs.push_back(crtc.get()); - - if (crtc->id() == e->crtc_id) - current_crtc = crtc.get(); + for (int i = 0; i < res->count_encoders; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto enc = DrmEncoder::CreateInstance(*this, res->encoders[i], i); + if (enc) { + encoders_.emplace_back(std::move(enc)); } - - std::unique_ptr<DrmEncoder> enc( - new DrmEncoder(e, current_crtc, possible_crtcs)); - possible_clones.push_back(e->possible_clones); - drmModeFreeEncoder(e); - - encoders_.emplace_back(std::move(enc)); } - for (unsigned int i = 0; i < encoders_.size(); i++) { - for (unsigned int j = 0; j < encoders_.size(); j++) - if (possible_clones[i] & (1 << j)) - encoders_[i]->AddPossibleClone(encoders_[j].get()); - } - - for (int i = 0; !ret && i < res->count_connectors; ++i) { - drmModeConnectorPtr c = drmModeGetConnector(fd(), res->connectors[i]); - if (!c) { - ALOGE("Failed to get connector %d", res->connectors[i]); - ret = -ENODEV; - break; - } - - std::vector<DrmEncoder *> possible_encoders; - DrmEncoder *current_encoder = NULL; - for (int j = 0; j < c->count_encoders; ++j) { - for (auto &encoder : encoders_) { - if (encoder->id() == c->encoders[j]) - possible_encoders.push_back(encoder.get()); - if (encoder->id() == c->encoder_id) - current_encoder = encoder.get(); - } - } + for (int i = 0; i < res->count_connectors; ++i) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto conn = DrmConnector::CreateInstance(*this, res->connectors[i], i); - std::unique_ptr<DrmConnector> conn( - new DrmConnector(this, c, current_encoder, possible_encoders)); - - drmModeFreeConnector(c); - - ret = conn->Init(); - if (ret) { - ALOGE("Init connector %d failed", res->connectors[i]); - break; + if (!conn) { + continue; } - if (conn->writeback()) + if (conn->IsWriteback()) { writeback_connectors_.emplace_back(std::move(conn)); - else + } else { connectors_.emplace_back(std::move(conn)); - } - - // Primary display priority: - // 1) vendor.hwc.drm.primary_display_order property - // 2) internal connectors - // 3) anything else - std::vector<DrmConnector *> - primary_candidates = make_primary_display_candidates(connectors_); - if (!primary_candidates.empty() && !found_primary) { - DrmConnector &conn = **std::begin(primary_candidates); - conn.set_display(num_displays); - displays_[num_displays] = num_displays; - ++num_displays; - found_primary = true; - } else { - ALOGE( - "Failed to find primary display from " - "\"vendor.hwc.drm.primary_display_order\" property"); - } - - // If no priority display were found then pick first available as primary and - // for the others assign consecutive display_numbers. - for (auto &conn : connectors_) { - if (conn->external() || conn->internal()) { - if (!found_primary) { - conn->set_display(num_displays); - displays_[num_displays] = num_displays; - found_primary = true; - ++num_displays; - } else if (conn->display() < 0) { - conn->set_display(num_displays); - displays_[num_displays] = num_displays; - ++num_displays; - } } } - if (res) - drmModeFreeResources(res); - - // Catch-all for the above loops - if (ret) - return std::make_tuple(ret, 0); - - drmModePlaneResPtr plane_res = drmModeGetPlaneResources(fd()); + auto plane_res = MakeDrmModePlaneResUnique(GetFd()); if (!plane_res) { ALOGE("Failed to get plane resources"); - return std::make_tuple(-ENOENT, 0); + return -ENOENT; } for (uint32_t i = 0; i < plane_res->count_planes; ++i) { - drmModePlanePtr p = drmModeGetPlane(fd(), plane_res->planes[i]); - if (!p) { - ALOGE("Failed to get plane %d", plane_res->planes[i]); - ret = -ENODEV; - break; - } - - std::unique_ptr<DrmPlane> plane(new DrmPlane(this, p)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto plane = DrmPlane::CreateInstance(*this, plane_res->planes[i]); - drmModeFreePlane(p); - - ret = plane->Init(); - if (ret) { - ALOGE("Init plane %d failed", plane_res->planes[i]); - break; + if (plane) { + planes_.emplace_back(std::move(plane)); } - - planes_.emplace_back(std::move(plane)); - } - drmModeFreePlaneResources(plane_res); - if (ret) - return std::make_tuple(ret, 0); - - ret = event_listener_.Init(); - if (ret) { - ALOGE("Can't initialize event listener %d", ret); - return std::make_tuple(ret, 0); } - for (auto &conn : connectors_) { - ret = CreateDisplayPipe(conn.get()); - if (ret) { - ALOGE("Failed CreateDisplayPipe %d with %d", conn->id(), ret); - return std::make_tuple(ret, 0); - } - if (!AttachWriteback(conn.get())) { - ALOGI("Display %d has writeback attach to it", conn->display()); - } - } - return std::make_tuple(ret, displays_.size()); -} - -bool DrmDevice::HandlesDisplay(int display) const { - return displays_.find(display) != displays_.end(); -} - -DrmConnector *DrmDevice::GetConnectorForDisplay(int display) const { - for (auto &conn : connectors_) { - if (conn->display() == display) - return conn.get(); - } - return NULL; -} - -DrmConnector *DrmDevice::GetWritebackConnectorForDisplay(int display) const { - for (auto &conn : writeback_connectors_) { - if (conn->display() == display) - return conn.get(); - } - return NULL; -} - -// TODO what happens when hotplugging -DrmConnector *DrmDevice::AvailableWritebackConnector(int display) const { - DrmConnector *writeback_conn = GetWritebackConnectorForDisplay(display); - DrmConnector *display_conn = GetConnectorForDisplay(display); - // If we have a writeback already attached to the same CRTC just use that, - // if possible. - if (display_conn && writeback_conn && - writeback_conn->encoder()->CanClone(display_conn->encoder())) - return writeback_conn; - - // Use another CRTC if available and doesn't have any connector - for (auto &crtc : crtcs_) { - if (crtc->display() == display) - continue; - display_conn = GetConnectorForDisplay(crtc->display()); - // If we have a display connected don't use it for writeback - if (display_conn && display_conn->state() == DRM_MODE_CONNECTED) - continue; - writeback_conn = GetWritebackConnectorForDisplay(crtc->display()); - if (writeback_conn) - return writeback_conn; - } - return NULL; -} - -DrmCrtc *DrmDevice::GetCrtcForDisplay(int display) const { - for (auto &crtc : crtcs_) { - if (crtc->display() == display) - return crtc.get(); - } - return NULL; -} - -DrmPlane *DrmDevice::GetPlane(uint32_t id) const { - for (auto &plane : planes_) { - if (plane->id() == id) - return plane.get(); - } - return NULL; -} - -const std::vector<std::unique_ptr<DrmCrtc>> &DrmDevice::crtcs() const { - return crtcs_; -} - -uint32_t DrmDevice::next_mode_id() { - return ++mode_id_; -} - -int DrmDevice::TryEncoderForDisplay(int display, DrmEncoder *enc) { - /* First try to use the currently-bound crtc */ - DrmCrtc *crtc = enc->crtc(); - if (crtc && crtc->can_bind(display)) { - crtc->set_display(display); - enc->set_crtc(crtc); - return 0; - } - - /* Try to find a possible crtc which will work */ - for (DrmCrtc *crtc : enc->possible_crtcs()) { - /* We've already tried this earlier */ - if (crtc == enc->crtc()) - continue; - - if (crtc->can_bind(display)) { - crtc->set_display(display); - enc->set_crtc(crtc); - return 0; - } - } - - /* We can't use the encoder, but nothing went wrong, try another one */ - return -EAGAIN; -} - -int DrmDevice::CreateDisplayPipe(DrmConnector *connector) { - int display = connector->display(); - /* Try to use current setup first */ - if (connector->encoder()) { - int ret = TryEncoderForDisplay(display, connector->encoder()); - if (!ret) { - return 0; - } else if (ret != -EAGAIN) { - ALOGE("Could not set mode %d/%d", display, ret); - return ret; - } - } - - for (DrmEncoder *enc : connector->possible_encoders()) { - int ret = TryEncoderForDisplay(display, enc); - if (!ret) { - connector->set_encoder(enc); - return 0; - } else if (ret != -EAGAIN) { - ALOGE("Could not set mode %d/%d", display, ret); - return ret; - } - } - ALOGE("Could not find a suitable encoder/crtc for display %d", - connector->display()); - return -ENODEV; -} - -// Attach writeback connector to the CRTC linked to the display_conn -int DrmDevice::AttachWriteback(DrmConnector *display_conn) { - DrmCrtc *display_crtc = display_conn->encoder()->crtc(); - if (GetWritebackConnectorForDisplay(display_crtc->display()) != NULL) { - ALOGE("Display already has writeback attach to it"); - return -EINVAL; - } - for (auto &writeback_conn : writeback_connectors_) { - if (writeback_conn->display() >= 0) - continue; - for (DrmEncoder *writeback_enc : writeback_conn->possible_encoders()) { - for (DrmCrtc *possible_crtc : writeback_enc->possible_crtcs()) { - if (possible_crtc != display_crtc) - continue; - // Use just encoders which had not been bound already - if (writeback_enc->can_bind(display_crtc->display())) { - writeback_enc->set_crtc(display_crtc); - writeback_conn->set_encoder(writeback_enc); - writeback_conn->set_display(display_crtc->display()); - writeback_conn->UpdateModes(); - return 0; - } - } - } - } - return -EINVAL; + return 0; } -int DrmDevice::CreatePropertyBlob(void *data, size_t length, - uint32_t *blob_id) { - struct drm_mode_create_blob create_blob; - memset(&create_blob, 0, sizeof(create_blob)); +auto DrmDevice::RegisterUserPropertyBlob(void *data, size_t length) const + -> DrmModeUserPropertyBlobUnique { + struct drm_mode_create_blob create_blob {}; create_blob.length = length; + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-cstyle-cast) create_blob.data = (__u64)data; - int ret = drmIoctl(fd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob); - if (ret) { + int ret = drmIoctl(GetFd(), DRM_IOCTL_MODE_CREATEPROPBLOB, &create_blob); + if (ret != 0) { ALOGE("Failed to create mode property blob %d", ret); - return ret; - } - *blob_id = create_blob.blob_id; - return 0; -} - -int DrmDevice::DestroyPropertyBlob(uint32_t blob_id) { - if (!blob_id) - return 0; - - struct drm_mode_destroy_blob destroy_blob; - memset(&destroy_blob, 0, sizeof(destroy_blob)); - destroy_blob.blob_id = (__u32)blob_id; - int ret = drmIoctl(fd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, &destroy_blob); - if (ret) { - ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", blob_id, ret); - return ret; - } - return 0; -} - -DrmEventListener *DrmDevice::event_listener() { - return &event_listener_; + return {}; + } + + return DrmModeUserPropertyBlobUnique( + new uint32_t(create_blob.blob_id), [this](const uint32_t *it) { + struct drm_mode_destroy_blob destroy_blob {}; + destroy_blob.blob_id = (__u32)*it; + int err = drmIoctl(GetFd(), DRM_IOCTL_MODE_DESTROYPROPBLOB, + &destroy_blob); + if (err != 0) { + ALOGE("Failed to destroy mode property blob %" PRIu32 "/%d", *it, + err); + } + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory) + delete it; + }); } int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type, - const char *prop_name, DrmProperty *property) { - drmModeObjectPropertiesPtr props; + const char *prop_name, DrmProperty *property) const { + drmModeObjectPropertiesPtr props = nullptr; - props = drmModeObjectGetProperties(fd(), obj_id, obj_type); - if (!props) { + props = drmModeObjectGetProperties(GetFd(), obj_id, obj_type); + if (props == nullptr) { ALOGE("Failed to get properties for %d/%x", obj_id, obj_type); return -ENODEV; } bool found = false; for (int i = 0; !found && (size_t)i < props->count_props; ++i) { - drmModePropertyPtr p = drmModeGetProperty(fd(), props->props[i]); - if (!strcmp(p->name, prop_name)) { - property->Init(p, props->prop_values[i]); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + drmModePropertyPtr p = drmModeGetProperty(GetFd(), props->props[i]); + if (strcmp(p->name, prop_name) == 0) { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + property->Init(obj_id, p, props->prop_values[i]); found = true; } drmModeFreeProperty(p); @@ -549,32 +192,51 @@ int DrmDevice::GetProperty(uint32_t obj_id, uint32_t obj_type, return found ? 0 : -ENOENT; } -int DrmDevice::GetPlaneProperty(const DrmPlane &plane, const char *prop_name, - DrmProperty *property) { - return GetProperty(plane.id(), DRM_MODE_OBJECT_PLANE, prop_name, property); +std::string DrmDevice::GetName() const { + auto *ver = drmGetVersion(GetFd()); + if (ver == nullptr) { + ALOGW("Failed to get drm version for fd=%d", GetFd()); + return "generic"; + } + + std::string name(ver->name); + drmFreeVersion(ver); + return name; +} + +auto DrmDevice::IsKMSDev(const char *path) -> bool { + auto fd = UniqueFd(open(path, O_RDWR | O_CLOEXEC)); + if (!fd) { + return false; + } + + auto res = MakeDrmModeResUnique(fd.Get()); + if (!res) { + return false; + } + + bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 && + res->count_encoders > 0; + + return is_kms; } -int DrmDevice::GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name, - DrmProperty *property) { - return GetProperty(crtc.id(), DRM_MODE_OBJECT_CRTC, prop_name, property); +auto DrmDevice::GetConnectors() + -> const std::vector<std::unique_ptr<DrmConnector>> & { + return connectors_; } -int DrmDevice::GetConnectorProperty(const DrmConnector &connector, - const char *prop_name, - DrmProperty *property) { - return GetProperty(connector.id(), DRM_MODE_OBJECT_CONNECTOR, prop_name, - property); +auto DrmDevice::GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> & { + return planes_; } -const std::string DrmDevice::GetName() const { - auto ver = drmGetVersion(fd_.get()); - if (!ver) { - ALOGW("Failed to get drm version for fd=%d", fd_.get()); - return "generic"; - } +auto DrmDevice::GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> & { + return crtcs_; +} - std::string name(ver->name); - drmFreeVersion(ver); - return name; +auto DrmDevice::GetEncoders() + -> const std::vector<std::unique_ptr<DrmEncoder>> & { + return encoders_; } + } // namespace android diff --git a/drm/DrmDevice.h b/drm/DrmDevice.h index be68aa6..f2530ee 100644 --- a/drm/DrmDevice.h +++ b/drm/DrmDevice.h @@ -17,93 +17,98 @@ #ifndef ANDROID_DRM_H_ #define ANDROID_DRM_H_ -#include <stdint.h> - +#include <cstdint> #include <map> #include <tuple> #include "DrmConnector.h" #include "DrmCrtc.h" #include "DrmEncoder.h" -#include "DrmEventListener.h" -#include "DrmPlane.h" +#include "DrmFbImporter.h" +#include "utils/UniqueFd.h" namespace android { +class DrmFbImporter; +class DrmPlane; + class DrmDevice { public: DrmDevice(); - ~DrmDevice(); + ~DrmDevice() = default; - std::tuple<int, int> Init(const char *path, int num_displays); + auto Init(const char *path) -> int; - int fd() const { - return fd_.get(); + auto GetFd() const { + return fd_.Get(); } - const std::vector<std::unique_ptr<DrmConnector>> &connectors() const { - return connectors_; + auto GetConnectors() -> const std::vector<std::unique_ptr<DrmConnector>> &; + auto GetPlanes() -> const std::vector<std::unique_ptr<DrmPlane>> &; + auto GetCrtcs() -> const std::vector<std::unique_ptr<DrmCrtc>> &; + auto GetEncoders() -> const std::vector<std::unique_ptr<DrmEncoder>> &; + + auto GetMinResolution() const { + return min_resolution_; } - const std::vector<std::unique_ptr<DrmPlane>> &planes() const { - return planes_; + auto GetMaxResolution() const { + return max_resolution_; } - std::pair<uint32_t, uint32_t> min_resolution() const { - return min_resolution_; + std::string GetName() const; + + auto RegisterUserPropertyBlob(void *data, size_t length) const + -> DrmModeUserPropertyBlobUnique; + + auto HasAddFb2ModifiersSupport() const { + return HasAddFb2ModifiersSupport_; } - std::pair<uint32_t, uint32_t> max_resolution() const { - return max_resolution_; + auto &GetDrmFbImporter() { + return *drm_fb_importer_; } - DrmConnector *GetConnectorForDisplay(int display) const; - DrmConnector *GetWritebackConnectorForDisplay(int display) const; - DrmConnector *AvailableWritebackConnector(int display) const; - DrmCrtc *GetCrtcForDisplay(int display) const; - DrmPlane *GetPlane(uint32_t id) const; - DrmEventListener *event_listener(); - - int GetPlaneProperty(const DrmPlane &plane, const char *prop_name, - DrmProperty *property); - int GetCrtcProperty(const DrmCrtc &crtc, const char *prop_name, - DrmProperty *property); - int GetConnectorProperty(const DrmConnector &connector, const char *prop_name, - DrmProperty *property); - - const std::string GetName() const; - - const std::vector<std::unique_ptr<DrmCrtc>> &crtcs() const; - uint32_t next_mode_id(); - - int CreatePropertyBlob(void *data, size_t length, uint32_t *blob_id); - int DestroyPropertyBlob(uint32_t blob_id); - bool HandlesDisplay(int display) const; - void RegisterHotplugHandler(DrmEventHandler *handler) { - event_listener_.RegisterHotplugHandler(handler); + static auto IsKMSDev(const char *path) -> bool; + + auto FindCrtcById(uint32_t id) const -> DrmCrtc * { + for (const auto &crtc : crtcs_) { + if (crtc->GetId() == id) { + return crtc.get(); + } + }; + + return nullptr; } - private: - int TryEncoderForDisplay(int display, DrmEncoder *enc); - int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name, - DrmProperty *property); + auto FindEncoderById(uint32_t id) const -> DrmEncoder * { + for (const auto &enc : encoders_) { + if (enc->GetId() == id) { + return enc.get(); + } + }; - int CreateDisplayPipe(DrmConnector *connector); - int AttachWriteback(DrmConnector *display_conn); + return nullptr; + } + int GetProperty(uint32_t obj_id, uint32_t obj_type, const char *prop_name, + DrmProperty *property) const; + + private: UniqueFd fd_; - uint32_t mode_id_ = 0; std::vector<std::unique_ptr<DrmConnector>> connectors_; std::vector<std::unique_ptr<DrmConnector>> writeback_connectors_; std::vector<std::unique_ptr<DrmEncoder>> encoders_; std::vector<std::unique_ptr<DrmCrtc>> crtcs_; std::vector<std::unique_ptr<DrmPlane>> planes_; - DrmEventListener event_listener_; std::pair<uint32_t, uint32_t> min_resolution_; std::pair<uint32_t, uint32_t> max_resolution_; - std::map<int, int> displays_; + + bool HasAddFb2ModifiersSupport_{}; + + std::unique_ptr<DrmFbImporter> drm_fb_importer_; }; } // namespace android diff --git a/drm/DrmDisplayPipeline.cpp b/drm/DrmDisplayPipeline.cpp new file mode 100644 index 0000000..f993d28 --- /dev/null +++ b/drm/DrmDisplayPipeline.cpp @@ -0,0 +1,192 @@ +/* + * Copyright (C) 2022 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. + */ + +#define LOG_TAG "hwc-drm-display-pipeline" + +#include "DrmDisplayPipeline.h" + +#include "DrmAtomicStateManager.h" +#include "DrmConnector.h" +#include "DrmCrtc.h" +#include "DrmDevice.h" +#include "DrmEncoder.h" +#include "DrmPlane.h" +#include "utils/log.h" +#include "utils/properties.h" + +namespace android { + +template <class O> +auto PipelineBindable<O>::BindPipeline(DrmDisplayPipeline *pipeline, + bool return_object_if_bound) + -> std::shared_ptr<BindingOwner<O>> { + auto owner_object = owner_object_.lock(); + if (owner_object) { + if (bound_pipeline_ == pipeline && return_object_if_bound) { + return owner_object; + } + + return {}; + } + owner_object = std::make_shared<BindingOwner<O>>(static_cast<O *>(this)); + + owner_object_ = owner_object; + bound_pipeline_ = pipeline; + return owner_object; +} + +static auto TryCreatePipeline(DrmDevice &dev, DrmConnector &connector, + DrmEncoder &enc, DrmCrtc &crtc) + -> std::unique_ptr<DrmDisplayPipeline> { + /* Check if resources are available */ + + auto pipe = std::make_unique<DrmDisplayPipeline>(); + pipe->device = &dev; + + pipe->connector = connector.BindPipeline(pipe.get()); + pipe->encoder = enc.BindPipeline(pipe.get()); + pipe->crtc = crtc.BindPipeline(pipe.get()); + + if (!pipe->connector || !pipe->encoder || !pipe->crtc) { + return {}; + } + + std::vector<DrmPlane *> primary_planes; + std::vector<DrmPlane *> overlay_planes; + + /* Attach necessary resources */ + auto display_planes = std::vector<DrmPlane *>(); + for (const auto &plane : dev.GetPlanes()) { + if (plane->IsCrtcSupported(crtc)) { + if (plane->GetType() == DRM_PLANE_TYPE_PRIMARY) { + primary_planes.emplace_back(plane.get()); + } else if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) { + overlay_planes.emplace_back(plane.get()); + } else { + ALOGI("Ignoring cursor plane %d", plane->GetId()); + } + } + } + + if (primary_planes.empty()) { + ALOGE("Primary plane for CRTC %d not found", crtc.GetId()); + return {}; + } + + if (primary_planes.size() > 1) { + ALOGE("Found more than 1 primary plane for CRTC %d", crtc.GetId()); + return {}; + } + + pipe->primary_plane = primary_planes[0]->BindPipeline(pipe.get()); + if (!pipe->primary_plane) { + ALOGE("Primary plane %d is already owned. Internal error.", + primary_planes[0]->GetId()); + return {}; + } + + pipe->atomic_state_manager = std::make_unique<DrmAtomicStateManager>( + pipe.get()); + + return pipe; +} + +static auto TryCreatePipelineUsingEncoder(DrmDevice &dev, DrmConnector &conn, + DrmEncoder &enc) + -> std::unique_ptr<DrmDisplayPipeline> { + /* First try to use the currently-bound crtc */ + auto *crtc = dev.FindCrtcById(enc.GetCurrentCrtcId()); + if (crtc != nullptr) { + auto pipeline = TryCreatePipeline(dev, conn, enc, *crtc); + if (pipeline) { + return pipeline; + } + } + + /* Try to find a possible crtc which will work */ + for (const auto &crtc : dev.GetCrtcs()) { + if (enc.SupportsCrtc(*crtc)) { + auto pipeline = TryCreatePipeline(dev, conn, enc, *crtc); + if (pipeline) { + return pipeline; + } + } + } + + /* We can't use this encoder, but nothing went wrong, try another one */ + return {}; +} + +auto DrmDisplayPipeline::CreatePipeline(DrmConnector &connector) + -> std::unique_ptr<DrmDisplayPipeline> { + auto &dev = connector.GetDev(); + /* Try to use current setup first */ + auto *encoder = dev.FindEncoderById(connector.GetCurrentEncoderId()); + + if (encoder != nullptr) { + auto pipeline = TryCreatePipelineUsingEncoder(dev, connector, *encoder); + if (pipeline) { + return pipeline; + } + } + + for (const auto &enc : dev.GetEncoders()) { + if (connector.SupportsEncoder(*enc)) { + auto pipeline = TryCreatePipelineUsingEncoder(dev, connector, *enc); + if (pipeline) { + return pipeline; + } + } + } + + ALOGE("Could not find a suitable encoder/crtc for connector %s", + connector.GetName().c_str()); + + return {}; +} + +static bool ReadUseOverlayProperty() { + char use_overlay_planes_prop[PROPERTY_VALUE_MAX]; + property_get("vendor.hwc.drm.use_overlay_planes", use_overlay_planes_prop, + "1"); + constexpr int kStrtolBase = 10; + return strtol(use_overlay_planes_prop, nullptr, kStrtolBase) != 0; +} + +auto DrmDisplayPipeline::GetUsablePlanes() + -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> { + std::vector<std::shared_ptr<BindingOwner<DrmPlane>>> planes; + planes.emplace_back(primary_plane); + + static bool use_overlay_planes = ReadUseOverlayProperty(); + + if (use_overlay_planes) { + for (const auto &plane : device->GetPlanes()) { + if (plane->IsCrtcSupported(*crtc->Get())) { + if (plane->GetType() == DRM_PLANE_TYPE_OVERLAY) { + auto op = plane->BindPipeline(this, true); + if (op) { + planes.emplace_back(op); + } + } + } + } + } + + return planes; +} + +} // namespace android diff --git a/drm/DrmDisplayPipeline.h b/drm/DrmDisplayPipeline.h new file mode 100644 index 0000000..7ec619e --- /dev/null +++ b/drm/DrmDisplayPipeline.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2022 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 ANDROID_DRMDISPLAYPIPELINE_H_ +#define ANDROID_DRMDISPLAYPIPELINE_H_ + +#include <memory> +#include <vector> + +namespace android { + +class DrmConnector; +class DrmDevice; +class DrmPlane; +class DrmCrtc; +class DrmEncoder; +class DrmAtomicStateManager; + +struct DrmDisplayPipeline; + +template <class O> +class BindingOwner; + +template <class O> +class PipelineBindable { + friend class BindingOwner<O>; + + public: + auto *GetPipeline() { + return bound_pipeline_; + } + + auto BindPipeline(DrmDisplayPipeline *pipeline, + bool return_object_if_bound = false) + -> std::shared_ptr<BindingOwner<O>>; + + private: + DrmDisplayPipeline *bound_pipeline_; + std::weak_ptr<BindingOwner<O>> owner_object_; +}; + +template <class B> +class BindingOwner { + public: + explicit BindingOwner(B *pb) : bindable_(pb){}; + ~BindingOwner() { + bindable_->bound_pipeline_ = nullptr; + } + + B *Get() { + return bindable_; + } + + private: + B *const bindable_; +}; + +struct DrmDisplayPipeline { + static auto CreatePipeline(DrmConnector &connector) + -> std::unique_ptr<DrmDisplayPipeline>; + + auto GetUsablePlanes() + -> std::vector<std::shared_ptr<BindingOwner<DrmPlane>>>; + + DrmDevice *device; + + std::shared_ptr<BindingOwner<DrmConnector>> connector; + std::shared_ptr<BindingOwner<DrmEncoder>> encoder; + std::shared_ptr<BindingOwner<DrmCrtc>> crtc; + std::shared_ptr<BindingOwner<DrmPlane>> primary_plane; + + std::unique_ptr<DrmAtomicStateManager> atomic_state_manager; +}; + +} // namespace android + +#endif diff --git a/drm/DrmEncoder.cpp b/drm/DrmEncoder.cpp index bcf0926..eed5b5f 100644 --- a/drm/DrmEncoder.cpp +++ b/drm/DrmEncoder.cpp @@ -14,49 +14,28 @@ * limitations under the License. */ +#define LOG_TAG "hwc-drm-encoder" + #include "DrmEncoder.h" -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> + #include "DrmDevice.h" +#include "utils/log.h" namespace android { -DrmEncoder::DrmEncoder(drmModeEncoderPtr e, DrmCrtc *current_crtc, - const std::vector<DrmCrtc *> &possible_crtcs) - : id_(e->encoder_id), - crtc_(current_crtc), - display_(-1), - possible_crtcs_(possible_crtcs) { -} - -uint32_t DrmEncoder::id() const { - return id_; -} - -DrmCrtc *DrmEncoder::crtc() const { - return crtc_; -} - -bool DrmEncoder::CanClone(DrmEncoder *possible_clone) { - return possible_clones_.find(possible_clone) != possible_clones_.end(); -} - -void DrmEncoder::AddPossibleClone(DrmEncoder *possible_clone) { - possible_clones_.insert(possible_clone); -} +auto DrmEncoder::CreateInstance(DrmDevice &dev, uint32_t encoder_id, + uint32_t index) -> std::unique_ptr<DrmEncoder> { + auto e = MakeDrmModeEncoderUnique(dev.GetFd(), encoder_id); + if (!e) { + ALOGE("Failed to get encoder %d", encoder_id); + return {}; + } -void DrmEncoder::set_crtc(DrmCrtc *crtc) { - crtc_ = crtc; - display_ = crtc->display(); + return std::unique_ptr<DrmEncoder>(new DrmEncoder(std::move(e), index)); } -int DrmEncoder::display() const { - return display_; -} - -bool DrmEncoder::can_bind(int display) const { - return display_ == -1 || display_ == display; -} } // namespace android diff --git a/drm/DrmEncoder.h b/drm/DrmEncoder.h index f4464d0..39a695c 100644 --- a/drm/DrmEncoder.h +++ b/drm/DrmEncoder.h @@ -17,43 +17,52 @@ #ifndef ANDROID_DRM_ENCODER_H_ #define ANDROID_DRM_ENCODER_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> #include <set> #include <vector> #include "DrmCrtc.h" +#include "DrmDisplayPipeline.h" namespace android { -class DrmEncoder { +class DrmEncoder : public PipelineBindable<DrmEncoder> { public: - DrmEncoder(drmModeEncoderPtr e, DrmCrtc *current_crtc, - const std::vector<DrmCrtc *> &possible_crtcs); + static auto CreateInstance(DrmDevice &dev, uint32_t encoder_id, + uint32_t index) -> std::unique_ptr<DrmEncoder>; + DrmEncoder(const DrmEncoder &) = delete; DrmEncoder &operator=(const DrmEncoder &) = delete; - uint32_t id() const; + auto GetId() const { + return enc_->encoder_id; + }; + + auto GetIndexInResArray() const { + return index_in_res_array_; + } - DrmCrtc *crtc() const; - void set_crtc(DrmCrtc *crtc); - bool can_bind(int display) const; - int display() const; + auto CanClone(DrmEncoder &encoder) { + return (enc_->possible_clones & (1 << encoder.GetIndexInResArray())) != 0; + } - const std::vector<DrmCrtc *> &possible_crtcs() const { - return possible_crtcs_; + auto SupportsCrtc(DrmCrtc &crtc) { + return (enc_->possible_crtcs & (1 << crtc.GetIndexInResArray())) != 0; + } + + auto GetCurrentCrtcId() { + return enc_->crtc_id; } - bool CanClone(DrmEncoder *encoder); - void AddPossibleClone(DrmEncoder *possible_clone); private: - uint32_t id_; - DrmCrtc *crtc_; - int display_; + DrmEncoder(DrmModeEncoderUnique enc, uint32_t index) + : enc_(std::move(enc)), index_in_res_array_(index){}; + + DrmModeEncoderUnique enc_; - std::vector<DrmCrtc *> possible_crtcs_; - std::set<DrmEncoder *> possible_clones_; + const uint32_t index_in_res_array_; }; } // namespace android diff --git a/drm/DrmEventListener.cpp b/drm/DrmEventListener.cpp deleted file mode 100644 index 3d95e28..0000000 --- a/drm/DrmEventListener.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (C) 2016 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. - */ - -#define LOG_TAG "hwc-drm-event-listener" - -#include "DrmEventListener.h" - -#include <assert.h> -#include <errno.h> -#include <hardware/hardware.h> -#include <hardware/hwcomposer.h> -#include <linux/netlink.h> -#include <log/log.h> -#include <sys/socket.h> -#include <xf86drm.h> - -#include "DrmDevice.h" - -namespace android { - -DrmEventListener::DrmEventListener(DrmDevice *drm) - : Worker("drm-event-listener", HAL_PRIORITY_URGENT_DISPLAY), drm_(drm) { -} - -int DrmEventListener::Init() { - uevent_fd_.Set(socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT)); - if (uevent_fd_.get() < 0) { - ALOGE("Failed to open uevent socket: %s", strerror(errno)); - return uevent_fd_.get(); - } - - struct sockaddr_nl addr; - memset(&addr, 0, sizeof(addr)); - addr.nl_family = AF_NETLINK; - addr.nl_pid = 0; - addr.nl_groups = 0xFFFFFFFF; - - int ret = bind(uevent_fd_.get(), (struct sockaddr *)&addr, sizeof(addr)); - if (ret) { - ALOGE("Failed to bind uevent socket: %s", strerror(errno)); - return -errno; - } - - FD_ZERO(&fds_); - FD_SET(drm_->fd(), &fds_); - FD_SET(uevent_fd_.get(), &fds_); - max_fd_ = std::max(drm_->fd(), uevent_fd_.get()); - - return InitWorker(); -} - -void DrmEventListener::RegisterHotplugHandler(DrmEventHandler *handler) { - assert(!hotplug_handler_); - hotplug_handler_.reset(handler); -} - -void DrmEventListener::FlipHandler(int /* fd */, unsigned int /* sequence */, - unsigned int tv_sec, unsigned int tv_usec, - void *user_data) { - DrmEventHandler *handler = (DrmEventHandler *)user_data; - if (!handler) - return; - - handler->HandleEvent((uint64_t)tv_sec * 1000 * 1000 + tv_usec); - delete handler; -} - -void DrmEventListener::UEventHandler() { - char buffer[1024]; - int ret; - - struct timespec ts; - uint64_t timestamp = 0; - ret = clock_gettime(CLOCK_MONOTONIC, &ts); - if (!ret) - timestamp = ts.tv_sec * 1000 * 1000 * 1000 + ts.tv_nsec; - else - ALOGE("Failed to get monotonic clock on hotplug %d", ret); - - while (true) { - ret = read(uevent_fd_.get(), &buffer, sizeof(buffer)); - if (ret == 0) { - return; - } else if (ret < 0) { - ALOGE("Got error reading uevent %d", ret); - return; - } - - if (!hotplug_handler_) - continue; - - bool drm_event = false, hotplug_event = false; - for (int i = 0; i < ret;) { - char *event = buffer + i; - if (strcmp(event, "DEVTYPE=drm_minor")) - drm_event = true; - else if (strcmp(event, "HOTPLUG=1")) - hotplug_event = true; - - i += strlen(event) + 1; - } - - if (drm_event && hotplug_event) - hotplug_handler_->HandleEvent(timestamp); - } -} - -void DrmEventListener::Routine() { - int ret; - do { - ret = select(max_fd_ + 1, &fds_, NULL, NULL, NULL); - } while (ret == -1 && errno == EINTR); - - if (FD_ISSET(drm_->fd(), &fds_)) { - drmEventContext event_context = - {.version = 2, - .vblank_handler = NULL, - .page_flip_handler = DrmEventListener::FlipHandler}; - drmHandleEvent(drm_->fd(), &event_context); - } - - if (FD_ISSET(uevent_fd_.get(), &fds_)) - UEventHandler(); -} -} // namespace android diff --git a/drm/DrmFbImporter.cpp b/drm/DrmFbImporter.cpp new file mode 100644 index 0000000..eeab076 --- /dev/null +++ b/drm/DrmFbImporter.cpp @@ -0,0 +1,155 @@ +/* + * Copyright (C) 2021 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. + */ + +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LOG_TAG "hwc-platform-drm-generic" + +#include "DrmFbImporter.h" + +#include <hardware/gralloc.h> +#include <xf86drm.h> +#include <xf86drmMode.h> + +#include <cinttypes> +#include <system_error> + +#include "utils/log.h" +#include "utils/properties.h" + +namespace android { + +auto DrmFbIdHandle::CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle, + DrmDevice &drm) + -> std::shared_ptr<DrmFbIdHandle> { + // NOLINTNEXTLINE(cppcoreguidelines-owning-memory): priv. constructor usage + std::shared_ptr<DrmFbIdHandle> local(new DrmFbIdHandle(drm)); + + local->gem_handles_[0] = first_gem_handle; + int32_t err = 0; + + /* Framebuffer object creation require gem handle for every used plane */ + for (size_t i = 1; i < local->gem_handles_.size(); i++) { + if (bo->prime_fds[i] > 0) { + if (bo->prime_fds[i] != bo->prime_fds[0]) { + err = drmPrimeFDToHandle(drm.GetFd(), bo->prime_fds[i], + &local->gem_handles_.at(i)); + if (err != 0) { + ALOGE("failed to import prime fd %d errno=%d", bo->prime_fds[i], + errno); + } + } else { + local->gem_handles_.at(i) = local->gem_handles_[0]; + } + } + } + + bool has_modifiers = bo->modifiers[0] != DRM_FORMAT_MOD_NONE && + bo->modifiers[0] != DRM_FORMAT_MOD_INVALID; + + if (!drm.HasAddFb2ModifiersSupport() && has_modifiers) { + ALOGE("No ADDFB2 with modifier support. Can't import modifier %" PRIu64, + bo->modifiers[0]); + local.reset(); + return local; + } + + /* Create framebuffer object */ + if (!has_modifiers) { + err = drmModeAddFB2(drm.GetFd(), bo->width, bo->height, bo->format, + &local->gem_handles_[0], &bo->pitches[0], + &bo->offsets[0], &local->fb_id_, 0); + } else { + err = drmModeAddFB2WithModifiers(drm.GetFd(), bo->width, bo->height, + bo->format, &local->gem_handles_[0], + &bo->pitches[0], &bo->offsets[0], + &bo->modifiers[0], &local->fb_id_, + DRM_MODE_FB_MODIFIERS); + } + if (err != 0) { + ALOGE("could not create drm fb %d", err); + local.reset(); + } + + return local; +} + +DrmFbIdHandle::~DrmFbIdHandle() { + /* Destroy framebuffer object */ + if (drmModeRmFB(drm_->GetFd(), fb_id_) != 0) { + ALOGE("Failed to rm fb"); + } + + /* Close GEM handles. + * + * WARNING: TODO(nobody): + * From Linux side libweston relies on libgbm to get KMS handle and never + * closes it (handle is closed by libgbm on buffer destruction) + * Probably we should offer similar approach to users (at least on user + * request via system properties) + */ + struct drm_gem_close gem_close {}; + for (size_t i = 0; i < gem_handles_.size(); i++) { + /* Don't close invalid handle. Close handle only once in cases + * where several YUV planes located in the single buffer. */ + if (gem_handles_[i] == 0 || + (i != 0 && gem_handles_[i] == gem_handles_[0])) { + continue; + } + gem_close.handle = gem_handles_[i]; + int32_t err = drmIoctl(drm_->GetFd(), DRM_IOCTL_GEM_CLOSE, &gem_close); + if (err != 0) { + ALOGE("Failed to close gem handle %d, errno: %d", gem_handles_[i], errno); + } + } +} + +auto DrmFbImporter::GetOrCreateFbId(hwc_drm_bo_t *bo) + -> std::shared_ptr<DrmFbIdHandle> { + /* Lookup DrmFbIdHandle in cache first. First handle serves as a cache key. */ + GemHandle first_handle = 0; + int32_t err = drmPrimeFDToHandle(drm_->GetFd(), bo->prime_fds[0], + &first_handle); + + if (err != 0) { + ALOGE("Failed to import prime fd %d ret=%d", bo->prime_fds[0], err); + return {}; + } + + auto drm_fb_id_cached = drm_fb_id_handle_cache_.find(first_handle); + + if (drm_fb_id_cached != drm_fb_id_handle_cache_.end()) { + if (auto drm_fb_id_handle_shared = drm_fb_id_cached->second.lock()) { + return drm_fb_id_handle_shared; + } + drm_fb_id_handle_cache_.erase(drm_fb_id_cached); + } + + /* Cleanup cached empty weak pointers */ + const int minimal_cleanup_size = 128; + if (drm_fb_id_handle_cache_.size() > minimal_cleanup_size) { + CleanupEmptyCacheElements(); + } + + /* No DrmFbIdHandle found in cache, create framebuffer object */ + auto fb_id_handle = DrmFbIdHandle::CreateInstance(bo, first_handle, *drm_); + if (fb_id_handle) { + drm_fb_id_handle_cache_[first_handle] = fb_id_handle; + } + + return fb_id_handle; +} + +} // namespace android diff --git a/drm/DrmFbImporter.h b/drm/DrmFbImporter.h new file mode 100644 index 0000000..7f17bbe --- /dev/null +++ b/drm/DrmFbImporter.h @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2021 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 DRM_DRMFBIMPORTER_H_ +#define DRM_DRMFBIMPORTER_H_ + +#include <drm/drm_fourcc.h> +#include <hardware/gralloc.h> + +#include <array> +#include <map> + +#include "drm/DrmDevice.h" +#include "drmhwcgralloc.h" + +#ifndef DRM_FORMAT_INVALID +#define DRM_FORMAT_INVALID 0 +#endif + +using GemHandle = uint32_t; + +namespace android { + +class DrmFbIdHandle { + public: + static auto CreateInstance(hwc_drm_bo_t *bo, GemHandle first_gem_handle, + DrmDevice &drm) -> std::shared_ptr<DrmFbIdHandle>; + + ~DrmFbIdHandle(); + DrmFbIdHandle(DrmFbIdHandle &&) = delete; + DrmFbIdHandle(const DrmFbIdHandle &) = delete; + auto operator=(const DrmFbIdHandle &) = delete; + auto operator=(DrmFbIdHandle &&) = delete; + + auto GetFbId [[nodiscard]] () const -> uint32_t { + return fb_id_; + } + + private: + explicit DrmFbIdHandle(DrmDevice &drm) : drm_(&drm){}; + + DrmDevice *const drm_; + + uint32_t fb_id_{}; + std::array<GemHandle, kHwcDrmBoMaxPlanes> gem_handles_{}; +}; + +class DrmFbImporter { + public: + explicit DrmFbImporter(DrmDevice &drm) : drm_(&drm){}; + ~DrmFbImporter() = default; + DrmFbImporter(const DrmFbImporter &) = delete; + DrmFbImporter(DrmFbImporter &&) = delete; + auto operator=(const DrmFbImporter &) = delete; + auto operator=(DrmFbImporter &&) = delete; + + auto GetOrCreateFbId(hwc_drm_bo_t *bo) -> std::shared_ptr<DrmFbIdHandle>; + + private: + void CleanupEmptyCacheElements() { + for (auto it = drm_fb_id_handle_cache_.begin(); + it != drm_fb_id_handle_cache_.end();) { + if (it->second.expired()) { + it = drm_fb_id_handle_cache_.erase(it); + } else { + ++it; + } + } + } + + DrmDevice *const drm_; + + std::map<GemHandle, std::weak_ptr<DrmFbIdHandle>> drm_fb_id_handle_cache_; +}; + +} // namespace android + +#endif diff --git a/drm/DrmGenericImporter.cpp b/drm/DrmGenericImporter.cpp deleted file mode 100644 index 8ab4fe5..0000000 --- a/drm/DrmGenericImporter.cpp +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2020 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. - */ - -#define LOG_TAG "hwc-platform-drm-generic" - -#include "DrmGenericImporter.h" - -#include <cutils/properties.h> -#include <gralloc_handle.h> -#include <hardware/gralloc.h> -#include <inttypes.h> -#include <log/log.h> -#include <xf86drm.h> -#include <xf86drmMode.h> - -namespace android { - -DrmGenericImporter::DrmGenericImporter(DrmDevice *drm) : drm_(drm) { - uint64_t cap_value = 0; - if (drmGetCap(drm_->fd(), DRM_CAP_ADDFB2_MODIFIERS, &cap_value)) { - ALOGE("drmGetCap failed. Fallback to no modifier support."); - cap_value = 0; - } - has_modifier_support_ = cap_value; -} - -DrmGenericImporter::~DrmGenericImporter() { -} - -int DrmGenericImporter::ImportBuffer(hwc_drm_bo_t *bo) { - int ret = drmPrimeFDToHandle(drm_->fd(), bo->prime_fds[0], - &bo->gem_handles[0]); - if (ret) { - ALOGE("failed to import prime fd %d ret=%d", bo->prime_fds[0], ret); - return ret; - } - - for (int i = 1; i < HWC_DRM_BO_MAX_PLANES; i++) { - int fd = bo->prime_fds[i]; - if (fd != 0) { - if (fd != bo->prime_fds[0]) { - ALOGE("Multiplanar FBs are not supported by this version of composer"); - return -ENOTSUP; - } - bo->gem_handles[i] = bo->gem_handles[0]; - } - } - - if (!has_modifier_support_ && bo->modifiers[0]) { - ALOGE("No ADDFB2 with modifier support. Can't import modifier %" PRIu64, - bo->modifiers[0]); - return -EINVAL; - } - - if (!bo->with_modifiers) - ret = drmModeAddFB2(drm_->fd(), bo->width, bo->height, bo->format, - bo->gem_handles, bo->pitches, bo->offsets, &bo->fb_id, - 0); - else - ret = drmModeAddFB2WithModifiers(drm_->fd(), bo->width, bo->height, - bo->format, bo->gem_handles, bo->pitches, - bo->offsets, bo->modifiers, &bo->fb_id, - bo->modifiers[0] ? DRM_MODE_FB_MODIFIERS - : 0); - - if (ret) { - ALOGE("could not create drm fb %d", ret); - return ret; - } - - ImportHandle(bo->gem_handles[0]); - - return ret; -} - -int DrmGenericImporter::ReleaseBuffer(hwc_drm_bo_t *bo) { - if (bo->fb_id) - if (drmModeRmFB(drm_->fd(), bo->fb_id)) - ALOGE("Failed to rm fb"); - - for (int i = 0; i < HWC_DRM_BO_MAX_PLANES; i++) { - if (!bo->gem_handles[i]) - continue; - - if (ReleaseHandle(bo->gem_handles[i])) { - ALOGE("Failed to release gem handle %d", bo->gem_handles[i]); - } else { - for (int j = i + 1; j < HWC_DRM_BO_MAX_PLANES; j++) - if (bo->gem_handles[j] == bo->gem_handles[i]) - bo->gem_handles[j] = 0; - bo->gem_handles[i] = 0; - } - } - return 0; -} - -int DrmGenericImporter::ImportHandle(uint32_t gem_handle) { - gem_refcount_[gem_handle]++; - - return 0; -} - -int DrmGenericImporter::ReleaseHandle(uint32_t gem_handle) { - if (--gem_refcount_[gem_handle]) - return 0; - - gem_refcount_.erase(gem_handle); - - return CloseHandle(gem_handle); -} - -int DrmGenericImporter::CloseHandle(uint32_t gem_handle) { - struct drm_gem_close gem_close; - - memset(&gem_close, 0, sizeof(gem_close)); - - gem_close.handle = gem_handle; - int ret = drmIoctl(drm_->fd(), DRM_IOCTL_GEM_CLOSE, &gem_close); - if (ret) - ALOGE("Failed to close gem handle %d %d", gem_handle, ret); - - return ret; -} -} // namespace android diff --git a/drm/DrmGenericImporter.h b/drm/DrmGenericImporter.h deleted file mode 100644 index ca53762..0000000 --- a/drm/DrmGenericImporter.h +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright (C) 2020 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 ANDROID_PLATFORM_DRM_GENERIC_H_ -#define ANDROID_PLATFORM_DRM_GENERIC_H_ - -#include <drm/drm_fourcc.h> -#include <hardware/gralloc.h> - -#include <map> - -#include "drm/DrmDevice.h" -#include "drmhwcgralloc.h" - -#ifndef DRM_FORMAT_INVALID -#define DRM_FORMAT_INVALID 0 -#endif - -namespace android { - -class Importer { - public: - virtual ~Importer() { - } - - // Imports the buffer referred to by handle into bo. - // - // Note: This can be called from a different thread than ReleaseBuffer. The - // implementation is responsible for ensuring thread safety. - virtual int ImportBuffer(hwc_drm_bo_t *bo) = 0; - - // Releases the buffer object (ie: does the inverse of ImportBuffer) - // - // Note: This can be called from a different thread than ImportBuffer. The - // implementation is responsible for ensuring thread safety. - virtual int ReleaseBuffer(hwc_drm_bo_t *bo) = 0; -}; - -class DrmGenericImporter : public Importer { - public: - DrmGenericImporter(DrmDevice *drm); - ~DrmGenericImporter() override; - - int ImportBuffer(hwc_drm_bo_t *bo) override; - int ReleaseBuffer(hwc_drm_bo_t *bo) override; - int ImportHandle(uint32_t gem_handle); - int ReleaseHandle(uint32_t gem_handle); - - protected: - DrmDevice *drm_; - - private: - int CloseHandle(uint32_t gem_handle); - std::map<uint32_t, int> gem_refcount_; - bool has_modifier_support_; -}; - -} // namespace android - -#endif diff --git a/drm/DrmMode.cpp b/drm/DrmMode.cpp index 6de671a..010ea1b 100644 --- a/drm/DrmMode.cpp +++ b/drm/DrmMode.cpp @@ -16,13 +16,14 @@ #include "DrmMode.h" +#include <cstring> + #include "DrmDevice.h" namespace android { DrmMode::DrmMode(drmModeModeInfoPtr m) - : id_(0), - clock_(m->clock), + : clock_(m->clock), h_display_(m->hdisplay), h_sync_start_(m->hsync_start), h_sync_end_(m->hsync_end), @@ -48,79 +49,56 @@ bool DrmMode::operator==(const drmModeModeInfo &m) const { v_scan_ == m.vscan && flags_ == m.flags && type_ == m.type; } -void DrmMode::ToDrmModeModeInfo(drm_mode_modeinfo *m) const { - m->clock = clock_; - m->hdisplay = h_display_; - m->hsync_start = h_sync_start_; - m->hsync_end = h_sync_end_; - m->htotal = h_total_; - m->hskew = h_skew_; - m->vdisplay = v_display_; - m->vsync_start = v_sync_start_; - m->vsync_end = v_sync_end_; - m->vtotal = v_total_; - m->vscan = v_scan_; - m->vrefresh = v_refresh_; - m->flags = flags_; - m->type = type_; - strncpy(m->name, name_.c_str(), DRM_DISPLAY_MODE_LEN); -} - -uint32_t DrmMode::id() const { - return id_; -} - -void DrmMode::set_id(uint32_t id) { - id_ = id; -} - uint32_t DrmMode::clock() const { return clock_; } -uint32_t DrmMode::h_display() const { +uint16_t DrmMode::h_display() const { return h_display_; } -uint32_t DrmMode::h_sync_start() const { +uint16_t DrmMode::h_sync_start() const { return h_sync_start_; } -uint32_t DrmMode::h_sync_end() const { +uint16_t DrmMode::h_sync_end() const { return h_sync_end_; } -uint32_t DrmMode::h_total() const { +uint16_t DrmMode::h_total() const { return h_total_; } -uint32_t DrmMode::h_skew() const { +uint16_t DrmMode::h_skew() const { return h_skew_; } -uint32_t DrmMode::v_display() const { +uint16_t DrmMode::v_display() const { return v_display_; } -uint32_t DrmMode::v_sync_start() const { +uint16_t DrmMode::v_sync_start() const { return v_sync_start_; } -uint32_t DrmMode::v_sync_end() const { +uint16_t DrmMode::v_sync_end() const { return v_sync_end_; } -uint32_t DrmMode::v_total() const { +uint16_t DrmMode::v_total() const { return v_total_; } -uint32_t DrmMode::v_scan() const { +uint16_t DrmMode::v_scan() const { return v_scan_; } float DrmMode::v_refresh() const { + if (clock_ == 0) { + return v_refresh_; + } // Always recalculate refresh to report correct float rate - return clock_ / (float)(v_total_ * h_total_) * 1000.0f; + return static_cast<float>(clock_) / (float)(v_total_ * h_total_) * 1000.0F; } uint32_t DrmMode::flags() const { @@ -132,6 +110,31 @@ uint32_t DrmMode::type() const { } std::string DrmMode::name() const { - return name_; + return name_ + "@" + std::to_string(v_refresh()); +} + +auto DrmMode::CreateModeBlob(const DrmDevice &drm) + -> DrmModeUserPropertyBlobUnique { + struct drm_mode_modeinfo drm_mode = { + .clock = clock_, + .hdisplay = h_display_, + .hsync_start = h_sync_start_, + .hsync_end = h_sync_end_, + .htotal = h_total_, + .hskew = h_skew_, + .vdisplay = v_display_, + .vsync_start = v_sync_start_, + .vsync_end = v_sync_end_, + .vtotal = v_total_, + .vscan = v_scan_, + .vrefresh = v_refresh_, + .flags = flags_, + .type = type_, + }; + strncpy(drm_mode.name, name_.c_str(), DRM_DISPLAY_MODE_LEN); + + return drm.RegisterUserPropertyBlob(&drm_mode, + sizeof(struct drm_mode_modeinfo)); } + } // namespace android diff --git a/drm/DrmMode.h b/drm/DrmMode.h index 313a8ea..20515f9 100644 --- a/drm/DrmMode.h +++ b/drm/DrmMode.h @@ -17,37 +17,38 @@ #ifndef ANDROID_DRM_MODE_H_ #define ANDROID_DRM_MODE_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> +#include <cstdio> #include <string> +#include "DrmUnique.h" + namespace android { +class DrmDevice; + class DrmMode { public: DrmMode() = default; - DrmMode(drmModeModeInfoPtr m); + explicit DrmMode(drmModeModeInfoPtr m); bool operator==(const drmModeModeInfo &m) const; - void ToDrmModeModeInfo(drm_mode_modeinfo *m) const; - - uint32_t id() const; - void set_id(uint32_t id); uint32_t clock() const; - uint32_t h_display() const; - uint32_t h_sync_start() const; - uint32_t h_sync_end() const; - uint32_t h_total() const; - uint32_t h_skew() const; - - uint32_t v_display() const; - uint32_t v_sync_start() const; - uint32_t v_sync_end() const; - uint32_t v_total() const; - uint32_t v_scan() const; + uint16_t h_display() const; + uint16_t h_sync_start() const; + uint16_t h_sync_end() const; + uint16_t h_total() const; + uint16_t h_skew() const; + + uint16_t v_display() const; + uint16_t v_sync_start() const; + uint16_t v_sync_end() const; + uint16_t v_total() const; + uint16_t v_scan() const; float v_refresh() const; uint32_t flags() const; @@ -55,23 +56,23 @@ class DrmMode { std::string name() const; - private: - uint32_t id_ = 0; + auto CreateModeBlob(const DrmDevice &drm) -> DrmModeUserPropertyBlobUnique; + private: uint32_t clock_ = 0; - uint32_t h_display_ = 0; - uint32_t h_sync_start_ = 0; - uint32_t h_sync_end_ = 0; - uint32_t h_total_ = 0; - uint32_t h_skew_ = 0; - - uint32_t v_display_ = 0; - uint32_t v_sync_start_ = 0; - uint32_t v_sync_end_ = 0; - uint32_t v_total_ = 0; - uint32_t v_scan_ = 0; - uint32_t v_refresh_ = 0; + uint16_t h_display_ = 0; + uint16_t h_sync_start_ = 0; + uint16_t h_sync_end_ = 0; + uint16_t h_total_ = 0; + uint16_t h_skew_ = 0; + + uint16_t v_display_ = 0; + uint16_t v_sync_start_ = 0; + uint16_t v_sync_end_ = 0; + uint16_t v_total_ = 0; + uint16_t v_scan_ = 0; + uint16_t v_refresh_ = 0; uint32_t flags_ = 0; uint32_t type_ = 0; diff --git a/drm/DrmPlane.cpp b/drm/DrmPlane.cpp index f994252..28f48f3 100644 --- a/drm/DrmPlane.cpp +++ b/drm/DrmPlane.cpp @@ -18,35 +18,49 @@ #include "DrmPlane.h" -#include <errno.h> -#include <log/log.h> -#include <stdint.h> - +#include <algorithm> +#include <cerrno> #include <cinttypes> +#include <cstdint> #include "DrmDevice.h" +#include "bufferinfo/BufferInfoGetter.h" +#include "utils/log.h" namespace android { -DrmPlane::DrmPlane(DrmDevice *drm, drmModePlanePtr p) - : drm_(drm), - id_(p->plane_id), - possible_crtc_mask_(p->possible_crtcs), - formats_(p->formats, p->formats + p->count_formats) { +auto DrmPlane::CreateInstance(DrmDevice &dev, uint32_t plane_id) + -> std::unique_ptr<DrmPlane> { + auto p = MakeDrmModePlaneUnique(dev.GetFd(), plane_id); + if (!p) { + ALOGE("Failed to get plane %d", plane_id); + return {}; + } + + auto plane = std::unique_ptr<DrmPlane>(new DrmPlane(dev, std::move(p))); + + if (plane->Init() != 0) { + ALOGE("Failed to init plane %d", plane_id); + return {}; + } + + return plane; } int DrmPlane::Init() { + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + formats_ = {plane_->formats, plane_->formats + plane_->count_formats}; + DrmProperty p; - int ret = drm_->GetPlaneProperty(*this, "type", &p); - if (ret) { - ALOGE("Could not get plane type property"); - return ret; + if (!GetPlaneProperty("type", p)) { + return -ENOTSUP; } - uint64_t type; + int ret = 0; + uint64_t type = 0; std::tie(ret, type) = p.value(); - if (ret) { + if (ret != 0) { ALOGE("Failed to get plane type property value"); return ret; } @@ -61,99 +75,115 @@ int DrmPlane::Init() { return -EINVAL; } - ret = drm_->GetPlaneProperty(*this, "CRTC_ID", &crtc_property_); - if (ret) { - ALOGE("Could not get CRTC_ID property"); - return ret; + if (!GetPlaneProperty("CRTC_ID", crtc_property_) || + !GetPlaneProperty("FB_ID", fb_property_) || + !GetPlaneProperty("CRTC_X", crtc_x_property_) || + !GetPlaneProperty("CRTC_Y", crtc_y_property_) || + !GetPlaneProperty("CRTC_W", crtc_w_property_) || + !GetPlaneProperty("CRTC_H", crtc_h_property_) || + !GetPlaneProperty("SRC_X", src_x_property_) || + !GetPlaneProperty("SRC_Y", src_y_property_) || + !GetPlaneProperty("SRC_W", src_w_property_) || + !GetPlaneProperty("SRC_H", src_h_property_)) { + return -ENOTSUP; } - ret = drm_->GetPlaneProperty(*this, "FB_ID", &fb_property_); - if (ret) { - ALOGE("Could not get FB_ID property"); - return ret; + GetPlaneProperty("zpos", zpos_property_, Presence::kOptional); + + if (GetPlaneProperty("rotation", rotation_property_, Presence::kOptional)) { + rotation_property_.AddEnumToMap("rotate-0", DrmHwcTransform::kIdentity, + transform_enum_map_); + rotation_property_.AddEnumToMap("rotate-90", DrmHwcTransform::kRotate90, + transform_enum_map_); + rotation_property_.AddEnumToMap("rotate-180", DrmHwcTransform::kRotate180, + transform_enum_map_); + rotation_property_.AddEnumToMap("rotate-270", DrmHwcTransform::kRotate270, + transform_enum_map_); + rotation_property_.AddEnumToMap("reflect-x", DrmHwcTransform::kFlipH, + transform_enum_map_); + rotation_property_.AddEnumToMap("reflect-y", DrmHwcTransform::kFlipV, + transform_enum_map_); } - ret = drm_->GetPlaneProperty(*this, "CRTC_X", &crtc_x_property_); - if (ret) { - ALOGE("Could not get CRTC_X property"); - return ret; - } + GetPlaneProperty("alpha", alpha_property_, Presence::kOptional); - ret = drm_->GetPlaneProperty(*this, "CRTC_Y", &crtc_y_property_); - if (ret) { - ALOGE("Could not get CRTC_Y property"); - return ret; + if (GetPlaneProperty("pixel blend mode", blend_property_, + Presence::kOptional)) { + blend_property_.AddEnumToMap("Pre-multiplied", DrmHwcBlending::kPreMult, + blending_enum_map_); + blend_property_.AddEnumToMap("Coverage", DrmHwcBlending::kCoverage, + blending_enum_map_); + blend_property_.AddEnumToMap("None", DrmHwcBlending::kNone, + blending_enum_map_); } - ret = drm_->GetPlaneProperty(*this, "CRTC_W", &crtc_w_property_); - if (ret) { - ALOGE("Could not get CRTC_W property"); - return ret; + GetPlaneProperty("IN_FENCE_FD", in_fence_fd_property_, Presence::kOptional); + + if (HasNonRgbFormat()) { + if (GetPlaneProperty("COLOR_ENCODING", color_encoding_propery_, + Presence::kOptional)) { + color_encoding_propery_.AddEnumToMap("ITU-R BT.709 YCbCr", + DrmHwcColorSpace::kItuRec709, + color_encoding_enum_map_); + color_encoding_propery_.AddEnumToMap("ITU-R BT.601 YCbCr", + DrmHwcColorSpace::kItuRec601, + color_encoding_enum_map_); + color_encoding_propery_.AddEnumToMap("ITU-R BT.2020 YCbCr", + DrmHwcColorSpace::kItuRec2020, + color_encoding_enum_map_); + } + + if (GetPlaneProperty("COLOR_RANGE", color_range_property_, + Presence::kOptional)) { + color_range_property_.AddEnumToMap("YCbCr full range", + DrmHwcSampleRange::kFullRange, + color_range_enum_map_); + color_range_property_.AddEnumToMap("YCbCr limited range", + DrmHwcSampleRange::kLimitedRange, + color_range_enum_map_); + } } - ret = drm_->GetPlaneProperty(*this, "CRTC_H", &crtc_h_property_); - if (ret) { - ALOGE("Could not get CRTC_H property"); - return ret; - } + return 0; +} - ret = drm_->GetPlaneProperty(*this, "SRC_X", &src_x_property_); - if (ret) { - ALOGE("Could not get SRC_X property"); - return ret; - } +bool DrmPlane::IsCrtcSupported(const DrmCrtc &crtc) const { + return ((1 << crtc.GetIndexInResArray()) & plane_->possible_crtcs) != 0; +} - ret = drm_->GetPlaneProperty(*this, "SRC_Y", &src_y_property_); - if (ret) { - ALOGE("Could not get SRC_Y property"); - return ret; +bool DrmPlane::IsValidForLayer(DrmHwcLayer *layer) { + if (!rotation_property_) { + if (layer->transform != DrmHwcTransform::kIdentity) { + ALOGV("No rotation property on plane %d", GetId()); + return false; + } + } else { + if (transform_enum_map_.count(layer->transform) == 0) { + ALOGV("Transform is not supported on plane %d", GetId()); + return false; + } } - ret = drm_->GetPlaneProperty(*this, "SRC_W", &src_w_property_); - if (ret) { - ALOGE("Could not get SRC_W property"); - return ret; + if (alpha_property_.id() == 0 && layer->alpha != UINT16_MAX) { + ALOGV("Alpha is not supported on plane %d", GetId()); + return false; } - ret = drm_->GetPlaneProperty(*this, "SRC_H", &src_h_property_); - if (ret) { - ALOGE("Could not get SRC_H property"); - return ret; + if (blending_enum_map_.count(layer->blending) == 0 && + layer->blending != DrmHwcBlending::kNone && + layer->blending != DrmHwcBlending::kPreMult) { + ALOGV("Blending is not supported on plane %d", GetId()); + return false; } - ret = drm_->GetPlaneProperty(*this, "zpos", &zpos_property_); - if (ret) - ALOGE("Could not get zpos property for plane %u", id()); - - ret = drm_->GetPlaneProperty(*this, "rotation", &rotation_property_); - if (ret) - ALOGE("Could not get rotation property"); - - ret = drm_->GetPlaneProperty(*this, "alpha", &alpha_property_); - if (ret) - ALOGI("Could not get alpha property"); - - ret = drm_->GetPlaneProperty(*this, "pixel blend mode", &blend_property_); - if (ret) - ALOGI("Could not get pixel blend mode property"); - - ret = drm_->GetPlaneProperty(*this, "IN_FENCE_FD", &in_fence_fd_property_); - if (ret) - ALOGI("Could not get IN_FENCE_FD property"); - - return 0; -} - -uint32_t DrmPlane::id() const { - return id_; -} - -bool DrmPlane::GetCrtcSupported(const DrmCrtc &crtc) const { - return !!((1 << crtc.pipe()) & possible_crtc_mask_); -} + uint32_t format = layer->buffer_info.format; + if (!IsFormatSupported(format)) { + ALOGV("Plane %d does not supports %c%c%c%c format", GetId(), format, + format >> 8, format >> 16, format >> 24); + return false; + } -uint32_t DrmPlane::type() const { - return type_; + return true; } bool DrmPlane::IsFormatSupported(uint32_t format) const { @@ -161,63 +191,130 @@ bool DrmPlane::IsFormatSupported(uint32_t format) const { std::end(formats_); } -const DrmProperty &DrmPlane::crtc_property() const { - return crtc_property_; +bool DrmPlane::HasNonRgbFormat() const { + return std::find_if_not(std::begin(formats_), std::end(formats_), + [](uint32_t format) { + return BufferInfoGetter::IsDrmFormatRgb(format); + }) != std::end(formats_); } -const DrmProperty &DrmPlane::fb_property() const { - return fb_property_; +static uint64_t ToDrmRotation(DrmHwcTransform transform) { + uint64_t rotation = 0; + if ((transform & DrmHwcTransform::kFlipH) != 0) + rotation |= DRM_MODE_REFLECT_X; + if ((transform & DrmHwcTransform::kFlipV) != 0) + rotation |= DRM_MODE_REFLECT_Y; + if ((transform & DrmHwcTransform::kRotate90) != 0) + rotation |= DRM_MODE_ROTATE_90; + else if ((transform & DrmHwcTransform::kRotate180) != 0) + rotation |= DRM_MODE_ROTATE_180; + else if ((transform & DrmHwcTransform::kRotate270) != 0) + rotation |= DRM_MODE_ROTATE_270; + else + rotation |= DRM_MODE_ROTATE_0; + + return rotation; } -const DrmProperty &DrmPlane::crtc_x_property() const { - return crtc_x_property_; +/* Convert float to 16.16 fixed point */ +static int To1616FixPt(float in) { + constexpr int kBitShift = 16; + return int(in * (1 << kBitShift)); } -const DrmProperty &DrmPlane::crtc_y_property() const { - return crtc_y_property_; -} +auto DrmPlane::AtomicSetState(drmModeAtomicReq &pset, DrmHwcLayer &layer, + uint32_t zpos, uint32_t crtc_id) -> int { + if (!layer.fb_id_handle) { + ALOGE("Expected a valid framebuffer for pset"); + return -EINVAL; + } -const DrmProperty &DrmPlane::crtc_w_property() const { - return crtc_w_property_; -} + if (zpos_property_ && !zpos_property_.is_immutable()) { + uint64_t min_zpos = 0; -const DrmProperty &DrmPlane::crtc_h_property() const { - return crtc_h_property_; -} + // Ignore ret and use min_zpos as 0 by default + std::tie(std::ignore, min_zpos) = zpos_property_.range_min(); -const DrmProperty &DrmPlane::src_x_property() const { - return src_x_property_; -} + if (!zpos_property_.AtomicSet(pset, zpos + min_zpos)) { + return -EINVAL; + } + } -const DrmProperty &DrmPlane::src_y_property() const { - return src_y_property_; -} + if (layer.acquire_fence && + !in_fence_fd_property_.AtomicSet(pset, layer.acquire_fence.Get())) { + return -EINVAL; + } -const DrmProperty &DrmPlane::src_w_property() const { - return src_w_property_; -} + if (!crtc_property_.AtomicSet(pset, crtc_id) || + !fb_property_.AtomicSet(pset, layer.fb_id_handle->GetFbId()) || + !crtc_x_property_.AtomicSet(pset, layer.display_frame.left) || + !crtc_y_property_.AtomicSet(pset, layer.display_frame.top) || + !crtc_w_property_.AtomicSet(pset, layer.display_frame.right - + layer.display_frame.left) || + !crtc_h_property_.AtomicSet(pset, layer.display_frame.bottom - + layer.display_frame.top) || + !src_x_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.left)) || + !src_y_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.top)) || + !src_w_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.right - + layer.source_crop.left)) || + !src_h_property_.AtomicSet(pset, To1616FixPt(layer.source_crop.bottom - + layer.source_crop.top))) { + return -EINVAL; + } -const DrmProperty &DrmPlane::src_h_property() const { - return src_h_property_; -} + if (rotation_property_ && + !rotation_property_.AtomicSet(pset, ToDrmRotation(layer.transform))) { + return -EINVAL; + } -const DrmProperty &DrmPlane::zpos_property() const { - return zpos_property_; -} + if (alpha_property_ && !alpha_property_.AtomicSet(pset, layer.alpha)) { + return -EINVAL; + } -const DrmProperty &DrmPlane::rotation_property() const { - return rotation_property_; -} + if (blending_enum_map_.count(layer.blending) != 0 && + !blend_property_.AtomicSet(pset, blending_enum_map_[layer.blending])) { + return -EINVAL; + } + + if (color_encoding_enum_map_.count(layer.color_space) != 0 && + !color_encoding_propery_ + .AtomicSet(pset, color_encoding_enum_map_[layer.color_space])) { + return -EINVAL; + } + + if (color_range_enum_map_.count(layer.sample_range) != 0 && + !color_range_property_ + .AtomicSet(pset, color_range_enum_map_[layer.sample_range])) { + return -EINVAL; + } -const DrmProperty &DrmPlane::alpha_property() const { - return alpha_property_; + return 0; } -const DrmProperty &DrmPlane::blend_property() const { - return blend_property_; +auto DrmPlane::AtomicDisablePlane(drmModeAtomicReq &pset) -> int { + if (!crtc_property_.AtomicSet(pset, 0) || !fb_property_.AtomicSet(pset, 0)) { + return -EINVAL; + } + + return 0; } -const DrmProperty &DrmPlane::in_fence_fd_property() const { - return in_fence_fd_property_; +auto DrmPlane::GetPlaneProperty(const char *prop_name, DrmProperty &property, + Presence presence) -> bool { + int err = drm_->GetProperty(GetId(), DRM_MODE_OBJECT_PLANE, prop_name, + &property); + if (err != 0) { + if (presence == Presence::kMandatory) { + ALOGE("Could not get mandatory property \"%s\" from plane %d", prop_name, + GetId()); + } else { + ALOGV("Could not get optional property \"%s\" from plane %d", prop_name, + GetId()); + } + return false; + } + + return true; } + } // namespace android diff --git a/drm/DrmPlane.h b/drm/DrmPlane.h index 16731a8..65f458f 100644 --- a/drm/DrmPlane.h +++ b/drm/DrmPlane.h @@ -17,57 +17,62 @@ #ifndef ANDROID_DRM_PLANE_H_ #define ANDROID_DRM_PLANE_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> #include <vector> #include "DrmCrtc.h" #include "DrmProperty.h" +#include "drmhwcomposer.h" namespace android { class DrmDevice; +struct DrmHwcLayer; -class DrmPlane { +class DrmPlane : public PipelineBindable<DrmPlane> { public: - DrmPlane(DrmDevice *drm, drmModePlanePtr p); DrmPlane(const DrmPlane &) = delete; DrmPlane &operator=(const DrmPlane &) = delete; - int Init(); + static auto CreateInstance(DrmDevice &dev, uint32_t plane_id) + -> std::unique_ptr<DrmPlane>; - uint32_t id() const; + bool IsCrtcSupported(const DrmCrtc &crtc) const; + bool IsValidForLayer(DrmHwcLayer *layer); - bool GetCrtcSupported(const DrmCrtc &crtc) const; - - uint32_t type() const; + auto GetType() const { + return type_; + } bool IsFormatSupported(uint32_t format) const; + bool HasNonRgbFormat() const; + + auto AtomicSetState(drmModeAtomicReq &pset, DrmHwcLayer &layer, uint32_t zpos, + uint32_t crtc_id) -> int; + auto AtomicDisablePlane(drmModeAtomicReq &pset) -> int; + auto &GetZPosProperty() const { + return zpos_property_; + } - const DrmProperty &crtc_property() const; - const DrmProperty &fb_property() const; - const DrmProperty &crtc_x_property() const; - const DrmProperty &crtc_y_property() const; - const DrmProperty &crtc_w_property() const; - const DrmProperty &crtc_h_property() const; - const DrmProperty &src_x_property() const; - const DrmProperty &src_y_property() const; - const DrmProperty &src_w_property() const; - const DrmProperty &src_h_property() const; - const DrmProperty &zpos_property() const; - const DrmProperty &rotation_property() const; - const DrmProperty &alpha_property() const; - const DrmProperty &blend_property() const; - const DrmProperty &in_fence_fd_property() const; + auto GetId() const { + return plane_->plane_id; + } private: - DrmDevice *drm_; - uint32_t id_; + DrmPlane(DrmDevice &dev, DrmModePlaneUnique plane) + : drm_(&dev), plane_(std::move(plane)){}; + DrmDevice *const drm_; + DrmModePlaneUnique plane_; - uint32_t possible_crtc_mask_; + enum class Presence { kOptional, kMandatory }; - uint32_t type_; + auto Init() -> int; + auto GetPlaneProperty(const char *prop_name, DrmProperty &property, + Presence presence = Presence::kMandatory) -> bool; + + uint32_t type_{}; std::vector<uint32_t> formats_; @@ -86,6 +91,13 @@ class DrmPlane { DrmProperty alpha_property_; DrmProperty blend_property_; DrmProperty in_fence_fd_property_; + DrmProperty color_encoding_propery_; + DrmProperty color_range_property_; + + std::map<DrmHwcBlending, uint64_t> blending_enum_map_; + std::map<DrmHwcColorSpace, uint64_t> color_encoding_enum_map_; + std::map<DrmHwcSampleRange, uint64_t> color_range_enum_map_; + std::map<DrmHwcTransform, uint64_t> transform_enum_map_; }; } // namespace android diff --git a/drm/DrmProperty.cpp b/drm/DrmProperty.cpp index b60a76e..32f1c62 100644 --- a/drm/DrmProperty.cpp +++ b/drm/DrmProperty.cpp @@ -14,15 +14,19 @@ * limitations under the License. */ +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define LOG_TAG "hwc-drm-property" + #include "DrmProperty.h" -#include <errno.h> -#include <stdint.h> #include <xf86drmMode.h> +#include <cerrno> +#include <cstdint> #include <string> #include "DrmDevice.h" +#include "utils/log.h" namespace android { @@ -30,28 +34,26 @@ DrmProperty::DrmPropertyEnum::DrmPropertyEnum(drm_mode_property_enum *e) : value_(e->value), name_(e->name) { } -DrmProperty::DrmPropertyEnum::~DrmPropertyEnum() { -} - -DrmProperty::DrmProperty(drmModePropertyPtr p, uint64_t value) - : id_(0), type_(DRM_PROPERTY_TYPE_INVALID), flags_(0), name_("") { - Init(p, value); +DrmProperty::DrmProperty(uint32_t obj_id, drmModePropertyPtr p, + uint64_t value) { + Init(obj_id, p, value); } -void DrmProperty::Init(drmModePropertyPtr p, uint64_t value) { +void DrmProperty::Init(uint32_t obj_id, drmModePropertyPtr p, uint64_t value) { + obj_id_ = obj_id; id_ = p->prop_id; flags_ = p->flags; name_ = p->name; value_ = value; for (int i = 0; i < p->count_values; ++i) - values_.push_back(p->values[i]); + values_.emplace_back(p->values[i]); for (int i = 0; i < p->count_enums; ++i) - enums_.push_back(DrmPropertyEnum(&p->enums[i])); + enums_.emplace_back(DrmPropertyEnum(&p->enums[i])); for (int i = 0; i < p->count_blobs; ++i) - blob_ids_.push_back(p->blob_ids[i]); + blob_ids_.emplace_back(p->blob_ids[i]); if (flags_ & DRM_MODE_PROP_RANGE) type_ = DRM_PROPERTY_TYPE_INT; @@ -61,6 +63,8 @@ void DrmProperty::Init(drmModePropertyPtr p, uint64_t value) { type_ = DRM_PROPERTY_TYPE_OBJECT; else if (flags_ & DRM_MODE_PROP_BLOB) type_ = DRM_PROPERTY_TYPE_BLOB; + else if (flags_ & DRM_MODE_PROP_BITMASK) + type_ = DRM_PROPERTY_TYPE_BITMASK; } uint32_t DrmProperty::id() const { @@ -75,7 +79,7 @@ std::tuple<int, uint64_t> DrmProperty::value() const { if (type_ == DRM_PROPERTY_TYPE_BLOB) return std::make_tuple(0, value_); - if (values_.size() == 0) + if (values_.empty()) return std::make_tuple(-ENOENT, 0); switch (type_) { @@ -91,6 +95,7 @@ std::tuple<int, uint64_t> DrmProperty::value() const { case DRM_PROPERTY_TYPE_OBJECT: return std::make_tuple(0, value_); + case DRM_PROPERTY_TYPE_BITMASK: default: return std::make_tuple(-EINVAL, 0); } @@ -107,7 +112,7 @@ bool DrmProperty::is_range() const { std::tuple<int, uint64_t> DrmProperty::range_min() const { if (!is_range()) return std::make_tuple(-EINVAL, 0); - if (values_.size() < 1) + if (values_.empty()) return std::make_tuple(-ENOENT, 0); return std::make_tuple(0, values_[0]); @@ -123,13 +128,28 @@ std::tuple<int, uint64_t> DrmProperty::range_max() const { } std::tuple<uint64_t, int> DrmProperty::GetEnumValueWithName( - std::string name) const { - for (auto it : enums_) { - if (it.name_.compare(name) == 0) { + const std::string &name) const { + for (const auto &it : enums_) { + if (it.name_ == name) { return std::make_tuple(it.value_, 0); } } return std::make_tuple(UINT64_MAX, -EINVAL); } + +auto DrmProperty::AtomicSet(drmModeAtomicReq &pset, uint64_t value) const + -> bool { + if (id_ == 0) { + ALOGE("AtomicSet() is called on non-initialized property!"); + return false; + } + if (drmModeAtomicAddProperty(&pset, obj_id_, id_, value) < 0) { + ALOGE("Failed to add obj_id: %u, prop_id: %u (%s) to pset", obj_id_, id_, + name_.c_str()); + return false; + } + return true; +} + } // namespace android diff --git a/drm/DrmProperty.h b/drm/DrmProperty.h index d293da3..26a7c38 100644 --- a/drm/DrmProperty.h +++ b/drm/DrmProperty.h @@ -17,9 +17,10 @@ #ifndef ANDROID_DRM_PROPERTY_H_ #define ANDROID_DRM_PROPERTY_H_ -#include <stdint.h> #include <xf86drmMode.h> +#include <cstdint> +#include <map> #include <string> #include <vector> @@ -30,18 +31,19 @@ enum DrmPropertyType { DRM_PROPERTY_TYPE_ENUM, DRM_PROPERTY_TYPE_OBJECT, DRM_PROPERTY_TYPE_BLOB, + DRM_PROPERTY_TYPE_BITMASK, DRM_PROPERTY_TYPE_INVALID, }; class DrmProperty { public: DrmProperty() = default; - DrmProperty(drmModePropertyPtr p, uint64_t value); + DrmProperty(uint32_t obj_id, drmModePropertyPtr p, uint64_t value); DrmProperty(const DrmProperty &) = delete; DrmProperty &operator=(const DrmProperty &) = delete; - void Init(drmModePropertyPtr p, uint64_t value); - std::tuple<uint64_t, int> GetEnumValueWithName(std::string name) const; + auto Init(uint32_t obj_id, drmModePropertyPtr p, uint64_t value) -> void; + std::tuple<uint64_t, int> GetEnumValueWithName(const std::string &name) const; uint32_t id() const; std::string name() const; @@ -53,16 +55,28 @@ class DrmProperty { std::tuple<int, uint64_t> range_min() const; std::tuple<int, uint64_t> range_max() const; + [[nodiscard]] auto AtomicSet(drmModeAtomicReq &pset, uint64_t value) const + -> bool; + + template <class E> + auto AddEnumToMap(const std::string &name, E key, std::map<E, uint64_t> &map) + -> bool; + + explicit operator bool() const { + return id_ != 0; + } + private: class DrmPropertyEnum { public: - DrmPropertyEnum(drm_mode_property_enum *e); - ~DrmPropertyEnum(); + explicit DrmPropertyEnum(drm_mode_property_enum *e); + ~DrmPropertyEnum() = default; uint64_t value_; std::string name_; }; + uint32_t obj_id_ = 0; uint32_t id_ = 0; DrmPropertyType type_ = DRM_PROPERTY_TYPE_INVALID; @@ -74,6 +88,21 @@ class DrmProperty { std::vector<DrmPropertyEnum> enums_; std::vector<uint32_t> blob_ids_; }; + +template <class E> +auto DrmProperty::AddEnumToMap(const std::string &name, E key, + std::map<E, uint64_t> &map) -> bool { + uint64_t enum_value = UINT64_MAX; + int err = 0; + std::tie(enum_value, err) = GetEnumValueWithName(name); + if (err == 0) { + map[key] = enum_value; + return true; + } + + return false; +} + } // namespace android #endif // ANDROID_DRM_PROPERTY_H_ diff --git a/drm/DrmUnique.h b/drm/DrmUnique.h new file mode 100644 index 0000000..282528b --- /dev/null +++ b/drm/DrmUnique.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2021 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 DRM_UNIQUE_H_ +#define DRM_UNIQUE_H_ + +#include <xf86drmMode.h> + +#include <functional> +#include <memory> + +template <typename T> +using DUniquePtr = std::unique_ptr<T, std::function<void(T *)>>; + +using DrmModeAtomicReqUnique = DUniquePtr<drmModeAtomicReq>; +auto inline MakeDrmModeAtomicReqUnique() { + return DrmModeAtomicReqUnique(drmModeAtomicAlloc(), [](drmModeAtomicReq *it) { + drmModeAtomicFree(it); + }); +}; + +using DrmModeConnectorUnique = DUniquePtr<drmModeConnector>; +auto inline MakeDrmModeConnectorUnique(int fd, uint32_t connector_id) { + return DrmModeConnectorUnique(drmModeGetConnector(fd, connector_id), + [](drmModeConnector *it) { + drmModeFreeConnector(it); + }); +} + +using DrmModeCrtcUnique = DUniquePtr<drmModeCrtc>; +auto inline MakeDrmModeCrtcUnique(int fd, uint32_t crtc_id) { + return DrmModeCrtcUnique(drmModeGetCrtc(fd, crtc_id), + [](drmModeCrtc *it) { drmModeFreeCrtc(it); }); +} + +using DrmModeEncoderUnique = DUniquePtr<drmModeEncoder>; +auto inline MakeDrmModeEncoderUnique(int fd, uint32_t encoder_id) { + return DrmModeEncoderUnique(drmModeGetEncoder(fd, encoder_id), + [](drmModeEncoder *it) { + drmModeFreeEncoder(it); + }); +} + +using DrmModePlaneUnique = DUniquePtr<drmModePlane>; +auto inline MakeDrmModePlaneUnique(int fd, uint32_t plane_id) { + return DrmModePlaneUnique(drmModeGetPlane(fd, plane_id), + [](drmModePlane *it) { drmModeFreePlane(it); }); +} + +using DrmModePlaneResUnique = DUniquePtr<drmModePlaneRes>; +auto inline MakeDrmModePlaneResUnique(int fd) { + return DrmModePlaneResUnique(drmModeGetPlaneResources(fd), + [](drmModePlaneRes *it) { + drmModeFreePlaneResources(it); + }); +} + +using DrmModeUserPropertyBlobUnique = DUniquePtr<uint32_t /*id*/>; + +using DrmModePropertyBlobUnique = DUniquePtr<drmModePropertyBlobRes>; +auto inline MakeDrmModePropertyBlobUnique(int fd, uint32_t blob_id) { + return DrmModePropertyBlobUnique(drmModeGetPropertyBlob(fd, blob_id), + [](drmModePropertyBlobRes *it) { + drmModeFreePropertyBlob(it); + }); +} + +using DrmModeResUnique = DUniquePtr<drmModeRes>; +auto inline MakeDrmModeResUnique(int fd) { + return DrmModeResUnique(drmModeGetResources(fd), + [](drmModeRes *it) { drmModeFreeResources(it); }); +} + +#endif diff --git a/drm/ResourceManager.cpp b/drm/ResourceManager.cpp index fc24aea..c8235e9 100644 --- a/drm/ResourceManager.cpp +++ b/drm/ResourceManager.cpp @@ -18,134 +18,167 @@ #include "ResourceManager.h" -#include <cutils/properties.h> -#include <log/log.h> +#include <fcntl.h> #include <sys/stat.h> +#include <ctime> #include <sstream> #include "bufferinfo/BufferInfoGetter.h" +#include "drm/DrmAtomicStateManager.h" +#include "drm/DrmDevice.h" +#include "drm/DrmDisplayPipeline.h" +#include "drm/DrmPlane.h" +#include "utils/log.h" +#include "utils/properties.h" namespace android { -ResourceManager::ResourceManager() : num_displays_(0), gralloc_(NULL) { +ResourceManager::ResourceManager( + PipelineToFrontendBindingInterface *p2f_bind_interface) + : frontend_interface_(p2f_bind_interface) { + if (uevent_listener_.Init() != 0) { + ALOGE("Can't initialize event listener"); + } +} + +ResourceManager::~ResourceManager() { + uevent_listener_.Exit(); } -int ResourceManager::Init() { +void ResourceManager::Init() { + if (initialized_) { + ALOGE("Already initialized"); + return; + } + char path_pattern[PROPERTY_VALUE_MAX]; // Could be a valid path or it can have at the end of it the wildcard % // which means that it will try open all devices until an error is met. int path_len = property_get("vendor.hwc.drm.device", path_pattern, "/dev/dri/card%"); - int ret = 0; if (path_pattern[path_len - 1] != '%') { - ret = AddDrmDevice(std::string(path_pattern)); + AddDrmDevice(std::string(path_pattern)); } else { path_pattern[path_len - 1] = '\0'; - for (int idx = 0; !ret; ++idx) { + for (int idx = 0;; ++idx) { std::ostringstream path; path << path_pattern << idx; - struct stat buf; - if (stat(path.str().c_str(), &buf)) { + struct stat buf {}; + if (stat(path.str().c_str(), &buf) != 0) break; - } else if (IsKMSDev(path.str().c_str())) { - ret = AddDrmDevice(path.str()); + + if (DrmDevice::IsKMSDev(path.str().c_str())) { + AddDrmDevice(path.str()); } } } - if (!num_displays_) { - ALOGE("Failed to initialize any displays"); - return ret ? -EINVAL : ret; - } - char scale_with_gpu[PROPERTY_VALUE_MAX]; property_get("vendor.hwc.drm.scale_with_gpu", scale_with_gpu, "0"); scale_with_gpu_ = bool(strncmp(scale_with_gpu, "0", 1)); - if (!BufferInfoGetter::GetInstance()) { + if (BufferInfoGetter::GetInstance() == nullptr) { ALOGE("Failed to initialize BufferInfoGetter"); - return -EINVAL; + return; } - return hw_get_module(GRALLOC_HARDWARE_MODULE_ID, - (const hw_module_t **)&gralloc_); + uevent_listener_.RegisterHotplugHandler([this] { + const std::lock_guard<std::mutex> lock(GetMainLock()); + UpdateFrontendDisplays(); + }); + + UpdateFrontendDisplays(); + + initialized_ = true; } -int ResourceManager::AddDrmDevice(std::string path) { - std::unique_ptr<DrmDevice> drm = std::make_unique<DrmDevice>(); - int displays_added, ret; - std::tie(ret, displays_added) = drm->Init(path.c_str(), num_displays_); - if (ret) - return ret; - std::shared_ptr<Importer> importer; - importer.reset(new DrmGenericImporter(drm.get())); - if (!importer) { - ALOGE("Failed to create importer instance"); - return -ENODEV; +void ResourceManager::DeInit() { + if (!initialized_) { + ALOGE("Not initialized"); + return; } - importers_.push_back(importer); + + uevent_listener_.RegisterHotplugHandler([] {}); + + DetachAllFrontendDisplays(); + drms_.clear(); + + initialized_ = false; +} + +int ResourceManager::AddDrmDevice(const std::string &path) { + auto drm = std::make_unique<DrmDevice>(); + int ret = drm->Init(path.c_str()); drms_.push_back(std::move(drm)); - num_displays_ += displays_added; return ret; } -DrmConnector *ResourceManager::AvailableWritebackConnector(int display) { - DrmDevice *drm_device = GetDrmDevice(display); - DrmConnector *writeback_conn = NULL; - if (drm_device) { - writeback_conn = drm_device->AvailableWritebackConnector(display); - if (writeback_conn) - return writeback_conn; - } - for (auto &drm : drms_) { - if (drm.get() == drm_device) - continue; - writeback_conn = drm->AvailableWritebackConnector(display); - if (writeback_conn) - return writeback_conn; - } - return writeback_conn; +auto ResourceManager::GetTimeMonotonicNs() -> int64_t { + struct timespec ts {}; + clock_gettime(CLOCK_MONOTONIC, &ts); + constexpr int64_t kNsInSec = 1000000000LL; + return int64_t(ts.tv_sec) * kNsInSec + int64_t(ts.tv_nsec); } -bool ResourceManager::IsKMSDev(const char *path) { - int fd = open(path, O_RDWR | O_CLOEXEC); - if (fd < 0) - return false; - - auto res = drmModeGetResources(fd); - if (!res) { - close(fd); - return false; +void ResourceManager::UpdateFrontendDisplays() { + auto ordered_connectors = GetOrderedConnectors(); + + for (auto *conn : ordered_connectors) { + conn->UpdateModes(); + bool connected = conn->IsConnected(); + bool attached = attached_pipelines_.count(conn) != 0; + + if (connected != attached) { + ALOGI("%s connector %s", connected ? "Attaching" : "Detaching", + conn->GetName().c_str()); + + if (connected) { + auto pipeline = DrmDisplayPipeline::CreatePipeline(*conn); + frontend_interface_->BindDisplay(pipeline.get()); + attached_pipelines_[conn] = std::move(pipeline); + } else { + auto &pipeline = attached_pipelines_[conn]; + frontend_interface_->UnbindDisplay(pipeline.get()); + attached_pipelines_.erase(conn); + } + } } + frontend_interface_->FinalizeDisplayBinding(); +} - bool is_kms = res->count_crtcs > 0 && res->count_connectors > 0 && - res->count_encoders > 0; +void ResourceManager::DetachAllFrontendDisplays() { + for (auto &p : attached_pipelines_) { + frontend_interface_->UnbindDisplay(p.second.get()); + } + attached_pipelines_.clear(); + frontend_interface_->FinalizeDisplayBinding(); +} - drmModeFreeResources(res); - close(fd); +auto ResourceManager::GetOrderedConnectors() -> std::vector<DrmConnector *> { + /* Put internal displays first then external to + * ensure Internal will take Primary slot + */ - return is_kms; -} + std::vector<DrmConnector *> ordered_connectors; -DrmDevice *ResourceManager::GetDrmDevice(int display) { for (auto &drm : drms_) { - if (drm->HandlesDisplay(display)) - return drm.get(); + for (const auto &conn : drm->GetConnectors()) { + if (conn->IsInternal()) { + ordered_connectors.emplace_back(conn.get()); + } + } } - return NULL; -} -std::shared_ptr<Importer> ResourceManager::GetImporter(int display) { - for (unsigned int i = 0; i < drms_.size(); i++) { - if (drms_[i]->HandlesDisplay(display)) - return importers_[i]; + for (auto &drm : drms_) { + for (const auto &conn : drm->GetConnectors()) { + if (conn->IsExternal()) { + ordered_connectors.emplace_back(conn.get()); + } + } } - return NULL; -} -const gralloc_module_t *ResourceManager::gralloc() { - return gralloc_; + return ordered_connectors; } } // namespace android diff --git a/drm/ResourceManager.h b/drm/ResourceManager.h index 7102cea..88ba878 100644 --- a/drm/ResourceManager.h +++ b/drm/ResourceManager.h @@ -17,43 +17,67 @@ #ifndef RESOURCEMANAGER_H #define RESOURCEMANAGER_H -#include <string.h> +#include <cstring> #include "DrmDevice.h" -#include "DrmGenericImporter.h" +#include "DrmDisplayPipeline.h" +#include "DrmFbImporter.h" +#include "UEventListener.h" namespace android { +class PipelineToFrontendBindingInterface { + public: + virtual ~PipelineToFrontendBindingInterface() = default; + virtual bool BindDisplay(DrmDisplayPipeline *); + virtual bool UnbindDisplay(DrmDisplayPipeline *); + virtual void FinalizeDisplayBinding(); +}; + class ResourceManager { public: - ResourceManager(); + explicit ResourceManager( + PipelineToFrontendBindingInterface *p2f_bind_interface); ResourceManager(const ResourceManager &) = delete; ResourceManager &operator=(const ResourceManager &) = delete; - int Init(); - DrmDevice *GetDrmDevice(int display); - std::shared_ptr<Importer> GetImporter(int display); - const gralloc_module_t *gralloc(); - DrmConnector *AvailableWritebackConnector(int display); - const std::vector<std::unique_ptr<DrmDevice>> &getDrmDevices() const { - return drms_; - } - int getDisplayCount() const { - return num_displays_; - } - bool ForcedScalingWithGpu() { + ResourceManager(const ResourceManager &&) = delete; + ResourceManager &&operator=(const ResourceManager &&) = delete; + ~ResourceManager(); + + void Init(); + + void DeInit(); + + bool ForcedScalingWithGpu() const { return scale_with_gpu_; } + auto &GetMainLock() { + return main_lock_; + } + + static auto GetTimeMonotonicNs() -> int64_t; + private: - int AddDrmDevice(std::string path); - static bool IsKMSDev(const char *path); + auto AddDrmDevice(std::string const &path) -> int; + auto GetOrderedConnectors() -> std::vector<DrmConnector *>; + void UpdateFrontendDisplays(); + void DetachAllFrontendDisplays(); - int num_displays_; std::vector<std::unique_ptr<DrmDevice>> drms_; - std::vector<std::shared_ptr<Importer>> importers_; - const gralloc_module_t *gralloc_; - bool scale_with_gpu_; + bool scale_with_gpu_{}; + + UEventListener uevent_listener_; + + std::mutex main_lock_; + + std::map<DrmConnector *, std::unique_ptr<DrmDisplayPipeline>> + attached_pipelines_; + + PipelineToFrontendBindingInterface *const frontend_interface_; + + bool initialized_{}; }; } // namespace android diff --git a/drm/UEventListener.cpp b/drm/UEventListener.cpp new file mode 100644 index 0000000..b56b8e1 --- /dev/null +++ b/drm/UEventListener.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2016 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. + */ + +#define LOG_TAG "hwc-uevent-listener" + +#include "UEventListener.h" + +#include <cerrno> + +#include "utils/log.h" + +/* Originally defined in system/core/libsystem/include/system/graphics.h as + * #define HAL_PRIORITY_URGENT_DISPLAY (-8)*/ +constexpr int kHalPriorityUrgentDisplay = -8; + +namespace android { + +UEventListener::UEventListener() + : Worker("uevent-listener", kHalPriorityUrgentDisplay){}; + +int UEventListener::Init() { + uevent_ = UEvent::CreateInstance(); + if (!uevent_) { + return -ENODEV; + } + + return InitWorker(); +} + +void UEventListener::Routine() { + while (true) { + auto uevent_str = uevent_->ReadNext(); + + if (!hotplug_handler_ || !uevent_str) + continue; + + bool drm_event = uevent_str->find("DEVTYPE=drm_minor") != std::string::npos; + bool hotplug_event = uevent_str->find("HOTPLUG=1") != std::string::npos; + + if (drm_event && hotplug_event) { + constexpr useconds_t kDelayAfterUeventUs = 200000; + /* We need some delay to ensure DrmConnector::UpdateModes() will query + * correct modes list, otherwise at least RPI4 board may report 0 modes */ + usleep(kDelayAfterUeventUs); + hotplug_handler_(); + } + } +} +} // namespace android diff --git a/drm/DrmEventListener.h b/drm/UEventListener.h index 9f9a4ba..c8b8582 100644 --- a/drm/DrmEventListener.h +++ b/drm/UEventListener.h @@ -14,51 +14,34 @@ * limitations under the License. */ -#ifndef ANDROID_DRM_EVENT_LISTENER_H_ -#define ANDROID_DRM_EVENT_LISTENER_H_ +#ifndef ANDROID_UEVENT_LISTENER_H_ +#define ANDROID_UEVENT_LISTENER_H_ -#include "autofd.h" +#include <functional> + +#include "utils/UEvent.h" #include "utils/Worker.h" namespace android { -class DrmDevice; - -class DrmEventHandler { - public: - DrmEventHandler() { - } - virtual ~DrmEventHandler() { - } - - virtual void HandleEvent(uint64_t timestamp_us) = 0; -}; - -class DrmEventListener : public Worker { +class UEventListener : public Worker { public: - DrmEventListener(DrmDevice *drm); - virtual ~DrmEventListener() { - } + UEventListener(); + ~UEventListener() override = default; int Init(); - void RegisterHotplugHandler(DrmEventHandler *handler); - - static void FlipHandler(int fd, unsigned int sequence, unsigned int tv_sec, - unsigned int tv_usec, void *user_data); + void RegisterHotplugHandler(std::function<void()> hotplug_handler) { + hotplug_handler_ = std::move(hotplug_handler); + } protected: - virtual void Routine(); + void Routine() override; private: - void UEventHandler(); - - fd_set fds_; - UniqueFd uevent_fd_; - int max_fd_ = -1; + std::unique_ptr<UEvent> uevent_; - DrmDevice *drm_; - std::unique_ptr<DrmEventHandler> hotplug_handler_; + std::function<void()> hotplug_handler_; }; } // namespace android diff --git a/drm/VSyncWorker.cpp b/drm/VSyncWorker.cpp index b2f7e5f..ed41189 100644 --- a/drm/VSyncWorker.cpp +++ b/drm/VSyncWorker.cpp @@ -18,44 +18,26 @@ #include "VSyncWorker.h" -#include <log/log.h> -#include <stdlib.h> -#include <time.h> #include <xf86drm.h> #include <xf86drmMode.h> -namespace android { - -VSyncWorker::VSyncWorker() - : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY), - drm_(NULL), - display_(-1), - enabled_(false), - last_timestamp_(-1) { -} +#include <cstdlib> +#include <cstring> +#include <ctime> -VSyncWorker::~VSyncWorker() { -} +#include "utils/log.h" -int VSyncWorker::Init(DrmDevice *drm, int display) { - drm_ = drm; - display_ = display; +namespace android { - return InitWorker(); -} +VSyncWorker::VSyncWorker() : Worker("vsync", HAL_PRIORITY_URGENT_DISPLAY){}; -void VSyncWorker::RegisterCallback(std::shared_ptr<VsyncCallback> callback) { - Lock(); - callback_ = callback; - Unlock(); -} +auto VSyncWorker::Init(DrmDisplayPipeline *pipe, + std::function<void(uint64_t /*timestamp*/)> callback) + -> int { + pipe_ = pipe; + callback_ = std::move(callback); -void VSyncWorker::RegisterClientCallback(hwc2_callback_data_t data, - hwc2_function_pointer_t hook) { - Lock(); - vsync_callback_data_ = data; - vsync_callback_hook_ = reinterpret_cast<HWC2_PFN_VSYNC>(hook); - Unlock(); + return InitWorker(); } void VSyncWorker::VSyncControl(bool enabled) { @@ -80,7 +62,7 @@ void VSyncWorker::VSyncControl(bool enabled) { * Thus, we must sleep until timestamp 687 to maintain phase with the last * timestamp. */ -int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) { +int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) const { if (last_timestamp_ < 0) return current + frame_ns; @@ -88,28 +70,29 @@ int64_t VSyncWorker::GetPhasedVSync(int64_t frame_ns, int64_t current) { last_timestamp_; } -static const int64_t kOneSecondNs = 1 * 1000 * 1000 * 1000; +static const int64_t kOneSecondNs = 1LL * 1000 * 1000 * 1000; int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) { - struct timespec vsync; + struct timespec vsync {}; int ret = clock_gettime(CLOCK_MONOTONIC, &vsync); + if (ret) + return ret; - float refresh = 60.0f; // Default to 60Hz refresh rate - DrmConnector *conn = drm_->GetConnectorForDisplay(display_); - if (conn && conn->active_mode().v_refresh() != 0.0f) - refresh = conn->active_mode().v_refresh(); - else - ALOGW("Vsync worker active with conn=%p refresh=%f\n", conn, - conn ? conn->active_mode().v_refresh() : 0.0f); + float refresh = 60.0F; // Default to 60Hz refresh rate + if (pipe_ != nullptr && + pipe_->connector->Get()->GetActiveMode().v_refresh() != 0.0F) { + refresh = pipe_->connector->Get()->GetActiveMode().v_refresh(); + } - int64_t phased_timestamp = GetPhasedVSync(kOneSecondNs / refresh, + int64_t phased_timestamp = GetPhasedVSync(kOneSecondNs / + static_cast<int>(refresh), vsync.tv_sec * kOneSecondNs + vsync.tv_nsec); vsync.tv_sec = phased_timestamp / kOneSecondNs; - vsync.tv_nsec = phased_timestamp - (vsync.tv_sec * kOneSecondNs); + vsync.tv_nsec = int(phased_timestamp - (vsync.tv_sec * kOneSecondNs)); do { - ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, NULL); - } while (ret == -1 && errno == EINTR); + ret = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &vsync, nullptr); + } while (ret == EINTR); if (ret) return ret; @@ -118,7 +101,7 @@ int VSyncWorker::SyntheticWaitVBlank(int64_t *timestamp) { } void VSyncWorker::Routine() { - int ret; + int ret = 0; Lock(); if (!enabled_) { @@ -129,28 +112,28 @@ void VSyncWorker::Routine() { } } - int display = display_; - std::shared_ptr<VsyncCallback> callback(callback_); + auto *pipe = pipe_; Unlock(); - DrmCrtc *crtc = drm_->GetCrtcForDisplay(display); - if (!crtc) { - ALOGE("Failed to get crtc for display"); - return; - } - uint32_t high_crtc = (crtc->pipe() << DRM_VBLANK_HIGH_CRTC_SHIFT); + ret = -EAGAIN; + int64_t timestamp = 0; + drmVBlank vblank{}; - drmVBlank vblank; - memset(&vblank, 0, sizeof(vblank)); - vblank.request.type = (drmVBlankSeqType)( - DRM_VBLANK_RELATIVE | (high_crtc & DRM_VBLANK_HIGH_CRTC_MASK)); - vblank.request.sequence = 1; + if (pipe != nullptr) { + uint32_t high_crtc = (pipe->crtc->Get()->GetIndexInResArray() + << DRM_VBLANK_HIGH_CRTC_SHIFT); - int64_t timestamp; - ret = drmWaitVBlank(drm_->fd(), &vblank); - if (ret == -EINTR) { - return; - } else if (ret) { + vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | + (high_crtc & + DRM_VBLANK_HIGH_CRTC_MASK)); + vblank.request.sequence = 1; + + ret = drmWaitVBlank(pipe->device->GetFd(), &vblank); + if (ret == -EINTR) + return; + } + + if (ret) { ret = SyntheticWaitVBlank(×tamp); if (ret) return; @@ -162,13 +145,9 @@ void VSyncWorker::Routine() { if (!enabled_) return; - if (callback) - callback->Callback(display, timestamp); - - Lock(); - if (enabled_ && vsync_callback_hook_ && vsync_callback_data_) - vsync_callback_hook_(vsync_callback_data_, display, timestamp); - Unlock(); + if (callback_) { + callback_(timestamp); + } last_timestamp_ = timestamp; } diff --git a/drm/VSyncWorker.h b/drm/VSyncWorker.h index 7454b51..1e6d39f 100644 --- a/drm/VSyncWorker.h +++ b/drm/VSyncWorker.h @@ -20,8 +20,10 @@ #include <hardware/hardware.h> #include <hardware/hwcomposer.h> #include <hardware/hwcomposer2.h> -#include <stdint.h> +#include <atomic> +#include <cstdint> +#include <functional> #include <map> #include "DrmDevice.h" @@ -29,22 +31,13 @@ namespace android { -class VsyncCallback { - public: - virtual ~VsyncCallback() { - } - virtual void Callback(int display, int64_t timestamp) = 0; -}; - class VSyncWorker : public Worker { public: VSyncWorker(); - ~VSyncWorker() override; + ~VSyncWorker() override = default; - int Init(DrmDevice *drm, int display); - void RegisterCallback(std::shared_ptr<VsyncCallback> callback); - void RegisterClientCallback(hwc2_callback_data_t data, - hwc2_function_pointer_t hook); + auto Init(DrmDisplayPipeline *pipe, + std::function<void(uint64_t /*timestamp*/)> callback) -> int; void VSyncControl(bool enabled); @@ -52,22 +45,14 @@ class VSyncWorker : public Worker { void Routine() override; private: - int64_t GetPhasedVSync(int64_t frame_ns, int64_t current); + int64_t GetPhasedVSync(int64_t frame_ns, int64_t current) const; int SyntheticWaitVBlank(int64_t *timestamp); - DrmDevice *drm_; - - // shared_ptr since we need to use this outside of the thread lock (to - // actually call the hook) and we don't want the memory freed until we're - // done - std::shared_ptr<VsyncCallback> callback_ = NULL; - - int display_; - std::atomic_bool enabled_; - int64_t last_timestamp_; + std::function<void(uint64_t /*timestamp*/)> callback_; - hwc2_callback_data_t vsync_callback_data_ = NULL; - HWC2_PFN_VSYNC vsync_callback_hook_ = NULL; + DrmDisplayPipeline *pipe_ = nullptr; + std::atomic_bool enabled_ = false; + int64_t last_timestamp_ = -1; }; } // namespace android |