blob: 01b341b57d32b7899a91fe62bd46775bb31ac9b7 [file] [log] [blame]
/*
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdint.h>
#include <stdlib.h>
#include <drm.h>
#include <drm/sde_drm.h>
#include <drm_logger.h>
#include <errno.h>
#include <stdlib.h>
#include <algorithm>
#include <map>
#include <set>
#include <utility>
#include <vector>
#include <iterator>
#include "drm_encoder.h"
#include "drm_utils.h"
namespace sde_drm {
using std::unique_ptr;
using std::map;
using std::pair;
using std::make_pair;
using std::set;
using std::distance;
#define __CLASS__ "DRMEncoderManager"
DRMEncoderManager::~DRMEncoderManager() {}
void DRMEncoderManager::Init(drmModeRes *resource) {
for (int i = 0; i < resource->count_encoders; i++) {
unique_ptr<DRMEncoder> encoder(new DRMEncoder(fd_));
drmModeEncoder *libdrm_encoder = drmModeGetEncoder(fd_, resource->encoders[i]);
if (!libdrm_encoder) {
DRM_LOGE("Critical error: drmModeGetEncoder() failed for encoder %d.", resource->encoders[i]);
continue;
}
encoder->InitAndParse(libdrm_encoder);
encoder_pool_[resource->encoders[i]] = std::move(encoder);
}
// TODO(user): Remove call when driver reporting of encoders is consistent across all use cases
InsertSecondaryDSI();
}
/*
* This API is a workaround for maintaining appropriate HW port numbers for displays on platforms
* that can dynamically change between single panel and secondary panel uses cases. It is required
* that the port # for a pixel stream remains consistent, so userspace must account for the
* possiblility of the secondary panel use case, even during single panel use case.
* Driver rearchitecture for encoder creation is under discussion for future chipsets to avoid the
* need of this function.
*/
void DRMEncoderManager::InsertSecondaryDSI() {
uint32_t first_dsi_id = 0;
bool second_dsi_found = false;
bool first_dsi_found = false;
for (auto encoder = encoder_pool_.begin(); encoder != encoder_pool_.end(); encoder++) {
std::unique_ptr<DRMEncoder> &encoder_ptr = encoder->second;
uint32_t encoder_type;
encoder_ptr->GetType(&encoder_type);
if (encoder_type == DRM_MODE_ENCODER_DSI) {
if (!first_dsi_found) {
encoder_ptr->GetId(&first_dsi_id);
first_dsi_found = true;
} else if (first_dsi_found) {
// Secondary panel use case is active, below logic is skipped
second_dsi_found = true;
break;
}
}
}
if (first_dsi_found && !second_dsi_found) {
// Single panel use case is active, inject new DSI encoder
uint32_t enc_id = first_dsi_id + 1;
unique_ptr<DRMEncoder> sec_dsi_enc(new DRMEncoder(fd_, enc_id, DRM_MODE_ENCODER_DSI));
encoder_pool_[enc_id] = std::move(sec_dsi_enc);
DRM_LOGI("Userspace has inserted secondary panel DSI encoder!");
} else {
DRM_LOGI("Userspace did not need to insert secondary panel DSI encoder, it is present.");
}
}
void DRMEncoderManager::DumpByID(uint32_t id) {
encoder_pool_.at(id)->Dump();
}
void DRMEncoderManager::DumpAll() {
for (auto &encoder : encoder_pool_) {
encoder.second->Dump();
}
}
int DRMEncoderManager::GetEncoderInfo(uint32_t encoder_id, DRMEncoderInfo *info) {
int ret = -ENODEV;
auto iter = encoder_pool_.find(encoder_id);
if (iter != encoder_pool_.end()) {
encoder_pool_[encoder_id]->GetInfo(info);
ret = 0;
}
return ret;
}
int DRMEncoderManager::GetEncoderList(std::vector<uint32_t> *encoder_ids) {
if (!encoder_ids) {
return -EINVAL;
}
encoder_ids->clear();
for (auto &encoder : encoder_pool_) {
encoder_ids->push_back(encoder.first);
}
return 0;
}
int DRMEncoderManager::Reserve(const std::set<uint32_t> &possible_encoders, DRMDisplayToken *token) {
int ret = -ENODEV;
for (auto encoder = encoder_pool_.begin(); encoder != encoder_pool_.end(); encoder++) {
const uint32_t &encoder_id = encoder->first;
if ((encoder->second)->GetStatus() == DRMStatus::FREE) {
// If encoder is found in the set, the encoder is a candidate for selection and the encoder
// type for display's connector and encoder-this-iteration are already matched implicitly
if (possible_encoders.find(encoder_id) != possible_encoders.end()) {
encoder->second->Lock();
token->encoder_id = encoder_id;
int encoder_index = distance(encoder_pool_.begin(), encoder) + 1; // 1-indexed port
// Port id format.
// Bit 7 --> Display type 0: Pluggable 1: BuiltIn X:Virtual.
// Bit 6 --> Pluggable: 0 for TMDS encoder, 1 for DPMST encoder.
// Builtin Or Virtual: X
// Bit 5-0 --> Encoder index.
uint32_t encoder_type;
encoder->second->GetType(&encoder_type);
token->hw_port = GetDisplayTypeCode(encoder_type) | encoder_index;
ret = 0;
break;
}
}
}
return ret;
}
int DRMEncoderManager::GetDisplayTypeCode(uint32_t encoder_type) {
int disp_info = 0x2;
switch (encoder_type) {
case DRM_MODE_ENCODER_TMDS:
disp_info = 0x0;
break;
case DRM_MODE_ENCODER_DPMST:
disp_info = 0x1;
break;
default:
break;
}
return (disp_info << 6);
}
void DRMEncoderManager::Free(DRMDisplayToken *token) {
auto iter = encoder_pool_.find(token->encoder_id);
if (iter != encoder_pool_.end()) {
iter->second->Unlock();
} else {
DRM_LOGW("Failed! encoder_id %u not found! Cleaning up token anyway...", token->encoder_id);
}
token->encoder_id = 0;
token->hw_port = 0;
}
int DRMEncoderManager::GetPossibleCrtcIndices(uint32_t encoder_id,
std::set<uint32_t> *possible_crtc_indices) {
return encoder_pool_[encoder_id]->GetPossibleCrtcIndices(possible_crtc_indices);
}
// ==============================================================================================//
#undef __CLASS__
#define __CLASS__ "DRMEncoder"
DRMEncoder::~DRMEncoder() {
if (drm_encoder_) {
drmModeFreeEncoder(drm_encoder_);
}
}
void DRMEncoder::GetInfo(DRMEncoderInfo *info) {
*info = encoder_info_;
}
void DRMEncoder::Lock() {
status_ = DRMStatus::BUSY;
}
void DRMEncoder::Unlock() {
status_ = DRMStatus::FREE;
}
void DRMEncoder::InitAndParse(drmModeEncoder *encoder) {
drm_encoder_ = encoder;
encoder_info_.type = drm_encoder_->encoder_type;
}
int DRMEncoder::GetPossibleCrtcIndices(std::set<uint32_t> *possible_crtc_indices) {
if (!possible_crtc_indices) {
return -EINVAL;
}
(*possible_crtc_indices).clear();
std::bitset<32> possible_crtcs = drm_encoder_->possible_crtcs;
for (uint32_t i = 0; i < possible_crtcs.size(); i++) {
if (possible_crtcs[i]) {
(*possible_crtc_indices).insert(i);
}
}
return 0;
}
void DRMEncoder::Dump() {
if (drm_encoder_) {
DRM_LOGI("[driver-reported] id: %u encoder_type: %u crtc id: %u possible_crtcs: %u"
"possible_clones: %u fd = %d",
drm_encoder_->encoder_id, drm_encoder_->encoder_type, drm_encoder_->crtc_id,
drm_encoder_->possible_crtcs, drm_encoder_->possible_clones, fd_);
} else {
DRM_LOGI("[userspace-only] id: %u encoder_type: %u fd = %d ", fake_id_, fake_type_, fd_);
}
}
} // namespace sde_drm