| /* |
| * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/ |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <algorithm> |
| |
| #include <cstdint> |
| |
| #include <log/log.h> |
| #include <cutils/properties.h> |
| |
| #include "display.h" |
| #include "format.h" |
| #include "hwc_dev.h" |
| |
| HWCDisplay::HWCDisplay(enum disp_role role) : |
| active_config(0), |
| role(role), |
| cb_procs(NULL), |
| is_dummy(false), |
| vsync_on(false), |
| blanked(true), |
| is_flip_pending(false) |
| { |
| } |
| |
| void HWCDisplay::setup_composition_pipes() |
| { |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| KMSDisplay* kdisp = &this->disp_link; |
| |
| int count = 0; |
| for (auto plane : kdisp->card->get_planes()) { |
| auto possible_crtcs = plane->get_possible_crtcs(); |
| if (std::find(possible_crtcs.begin(), possible_crtcs.end(), kdisp->crtc) != possible_crtcs.end()) { |
| planeProps[count].plane = plane; |
| ALOGI("Overlay %d: plane_id: %d", count, planeProps[count].plane->id()); |
| count++; |
| } |
| } |
| } |
| |
| /* Page flip handler callback */ |
| void HWCDisplay::page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) |
| { |
| HWCDisplay* display = static_cast<HWCDisplay*>(data); |
| |
| std::unique_lock<std::mutex> lock(display->mutex); |
| |
| if (display->is_flip_pending == false) { |
| ALOGW("Spurious page flip?"); |
| return; |
| } |
| |
| /* release the old buffers */ |
| for (auto current_fb_info : display->current_fb_infos) |
| delete current_fb_info; |
| display->current_fb_infos.clear(); |
| |
| /* pending are now current */ |
| for (auto pending_fb_info : display->pending_fb_infos) |
| display->current_fb_infos.push_back(pending_fb_info); |
| display->pending_fb_infos.clear(); |
| |
| display->is_flip_pending = false; |
| lock.unlock(); |
| display->cond_flip.notify_one(); |
| } |
| |
| static int vblank_kick(HWCDisplay* display) |
| { |
| drmVBlank vblank; |
| memset(&vblank, 0, sizeof(vblank)); |
| vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT | |
| display->role == DISP_ROLE_SECONDARY ? DRM_VBLANK_SECONDARY : 0); |
| vblank.request.sequence = 1; |
| vblank.request.signal = (unsigned long)display; |
| int err = drmWaitVBlank(display->disp_link.card->fd(), &vblank); |
| if (err < 0) { |
| /* FIXME: error in drmWaitVBlank() use SW vsync instead? */ |
| ALOGE("drmWaitVBlank error %d (%s)", err, strerror(errno)); |
| return -1; |
| } |
| |
| return 0; |
| } |
| |
| /* Callback function that gets triggered on vsync */ |
| void HWCDisplay::vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data) |
| { |
| HWCDisplay* display = static_cast<HWCDisplay*>(data); |
| |
| std::unique_lock<std::mutex> lock(display->mutex); |
| |
| if (display->vsync_on) { |
| int64_t ts = sec * (int64_t)1000000000 + usec * (int64_t)1000; |
| |
| // ALOGD("Sending VBLANK at %lld for display %d", ts, display->role); |
| display->cb_procs->vsync(display->cb_procs, display->role, ts); |
| |
| vblank_kick(display); |
| } |
| } |
| |
| int HWCDisplay::set_vsync_state(bool state) |
| { |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| if (this->is_dummy) |
| return 0; |
| |
| this->vsync_on = state; |
| |
| if (this->vsync_on) |
| return vblank_kick(this); |
| |
| return 0; |
| } |
| |
| static void set_plane_properties(kms::AtomicReq& req, drm_plane_props_t* plane_props) |
| { |
| kms::Plane* plane = plane_props->plane; |
| |
| req.add(plane, "FB_ID", plane_props->fb_info->fb_id); |
| |
| req.add(plane, "IN_FENCE_FD", plane_props->layer->acquireFenceFd); |
| |
| req.add(plane, { |
| { "CRTC_ID", plane_props->crtc_id }, |
| { "SRC_X", (plane_props->src_x) << 16 }, |
| { "SRC_Y", (plane_props->src_y) << 16 }, |
| { "SRC_W", (plane_props->src_w) << 16 }, |
| { "SRC_H", (plane_props->src_h) << 16 }, |
| { "CRTC_X", plane_props->crtc_x }, |
| { "CRTC_Y", plane_props->crtc_y }, |
| { "CRTC_W", plane_props->crtc_w }, |
| { "CRTC_H", plane_props->crtc_h }, |
| }); |
| } |
| |
| int HWCDisplay::update_display(drm_plane_props_t* planeProp) |
| { |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| this->cond_flip.wait(lock, [this]{return !this->is_flip_pending;}); |
| |
| buffer_handle_t handle = planeProp->layer->handle; |
| if (!handle) { |
| ALOGW("Got empty handle, nothing to display"); |
| return 0; |
| } |
| |
| KMSDisplay* kdisp = &this->disp_link; |
| |
| planeProp->fb_info = new DRMFramebuffer(kdisp->card->fd(), handle, false); |
| this->pending_fb_infos.push_back(planeProp->fb_info); |
| |
| planeProp->crtc_id = kdisp->crtc->id(); |
| |
| // Atomic display update |
| kms::AtomicReq req(*kdisp->card); |
| set_plane_properties(req, planeProp); |
| int ret = req.commit(this, true); |
| if (ret) { |
| ALOGE("cannot do atomic commit/page flip: %d (%s)", ret, strerror(errno)); |
| for (auto pending_fb_info : this->pending_fb_infos) |
| delete pending_fb_info; |
| this->pending_fb_infos.clear(); |
| return ret; |
| } |
| |
| // ALOGD("Scheduled page flip on display %d", this->role); |
| this->is_flip_pending = true; |
| |
| return 0; |
| } |
| |
| void HWCDisplay::blank(int blank) |
| { |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| if (this->is_dummy) |
| return; |
| |
| KMSDisplay* kdisp = &this->disp_link; |
| |
| ALOGI("Linking connector %d to crtc %d", kdisp->con->id(), kdisp->crtc->id()); |
| |
| int ret = kdisp->crtc->set_mode(kdisp->con, kdisp->mode); |
| if (ret) { |
| ALOGE("Failed to set crtc mode (%s)", strerror(-ret)); |
| return; |
| } |
| |
| // FIXME: This should actually blank the screen |
| this->blanked = blank; |
| } |
| |
| int HWCDisplay::get_display_configs(uint32_t* configs, size_t* numConfigs) |
| { |
| if (!configs || !numConfigs) |
| return -EINVAL; |
| |
| if (*numConfigs == 0) |
| return 0; |
| |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| size_t num = this->configs.size(); |
| if (num > *numConfigs) |
| num = *numConfigs; |
| |
| for (size_t i = 0; i < num; i++) |
| configs[i] = i; |
| |
| *numConfigs = num; |
| |
| return 0; |
| } |
| |
| int HWCDisplay::get_display_attributes(uint32_t cfg, const uint32_t* attributes, int32_t* values) |
| { |
| if (!attributes || !values) |
| return 0; |
| |
| std::unique_lock<std::mutex> lock(this->mutex); |
| |
| if (cfg >= this->configs.size()) |
| return -EINVAL; |
| |
| display_config_t* config = &this->configs[cfg]; |
| |
| for (size_t i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) { |
| switch (attributes[i]) { |
| case HWC_DISPLAY_VSYNC_PERIOD: |
| values[i] = 1000000000 / config->fps; |
| break; |
| case HWC_DISPLAY_WIDTH: |
| values[i] = config->xres; |
| break; |
| case HWC_DISPLAY_HEIGHT: |
| values[i] = config->yres; |
| break; |
| case HWC_DISPLAY_DPI_X: |
| values[i] = 1000 * config->xdpi; |
| break; |
| case HWC_DISPLAY_DPI_Y: |
| values[i] = 1000 * config->ydpi; |
| break; |
| } |
| } |
| |
| return 0; |
| } |