1/*
2* Copyright (c) 2017, The Linux Foundation. All rights reserved.
3*
4* Redistribution and use in source and binary forms, with or without
5* modification, are permitted provided that the following conditions are
6* met:
7*     * Redistributions of source code must retain the above copyright
8*       notice, this list of conditions and the following disclaimer.
9*     * Redistributions in binary form must reproduce the above
10*       copyright notice, this list of conditions and the following
11*       disclaimer in the documentation and/or other materials provided
12*       with the distribution.
13*     * Neither the name of The Linux Foundation nor the names of its
14*       contributors may be used to endorse or promote products derived
15*       from this software without specific prior written permission.
16*
17* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20* ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28*/
29
30#include <drm_master.h>
31#include <errno.h>
32#include <fcntl.h>
33#include <math.h>
34#include <pthread.h>
35#include <stdio.h>
36#include <stdlib.h>
37#include <sys/prctl.h>
38#include <sys/resource.h>
39#include <sys/types.h>
40#include <utils/debug.h>
41#include <utils/sys.h>
42#include <xf86drm.h>
43
44#include <algorithm>
45#include <map>
46#include <utility>
47#include <vector>
48
49#include "hw_events_drm.h"
50
51#define __CLASS__ "HWEventsDRM"
52
53namespace sdm {
54
55using drm_utils::DRMMaster;
56
57DisplayError HWEventsDRM::InitializePollFd() {
58  for (uint32_t i = 0; i < event_data_list_.size(); i++) {
59    char data[kMaxStringLength]{};
60    HWEventData &event_data = event_data_list_[i];
61    poll_fds_[i].fd = -1;
62
63    switch (event_data.event_type) {
64      case HWEvent::VSYNC: {
65        poll_fds_[i].events = POLLIN | POLLPRI | POLLERR;
66        DRMMaster *master = nullptr;
67        int ret = DRMMaster::GetInstance(&master);
68        if (ret < 0) {
69          DLOGE("Failed to acquire DRMMaster instance");
70          return kErrorNotSupported;
71        }
72        master->GetHandle(&poll_fds_[i].fd);
73        vsync_index_ = i;
74      } break;
75      case HWEvent::EXIT: {
76        // Create an eventfd to be used to unblock the poll system call when
77        // a thread is exiting.
78        poll_fds_[i].fd = Sys::eventfd_(0, 0);
79        poll_fds_[i].events |= POLLIN;
80        // Clear any existing data
81        Sys::pread_(poll_fds_[i].fd, data, kMaxStringLength, 0);
82      } break;
83      case HWEvent::IDLE_NOTIFY:
84      case HWEvent::CEC_READ_MESSAGE:
85      case HWEvent::SHOW_BLANK_EVENT:
86      case HWEvent::THERMAL_LEVEL:
87        break;
88    }
89  }
90
91  return kErrorNone;
92}
93
94DisplayError HWEventsDRM::SetEventParser() {
95  DisplayError error = kErrorNone;
96
97  for (auto &event_data : event_data_list_) {
98    switch (event_data.event_type) {
99      case HWEvent::VSYNC:
100        event_data.event_parser = &HWEventsDRM::HandleVSync;
101        break;
102      case HWEvent::IDLE_NOTIFY:
103        event_data.event_parser = &HWEventsDRM::HandleIdleTimeout;
104        break;
105      case HWEvent::CEC_READ_MESSAGE:
106        event_data.event_parser = &HWEventsDRM::HandleCECMessage;
107        break;
108      case HWEvent::EXIT:
109        event_data.event_parser = &HWEventsDRM::HandleThreadExit;
110        break;
111      case HWEvent::SHOW_BLANK_EVENT:
112        event_data.event_parser = &HWEventsDRM::HandleBlank;
113        break;
114      case HWEvent::THERMAL_LEVEL:
115        event_data.event_parser = &HWEventsDRM::HandleThermal;
116        break;
117      default:
118        error = kErrorParameters;
119        break;
120    }
121  }
122
123  return error;
124}
125
126void HWEventsDRM::PopulateHWEventData(const vector<HWEvent> &event_list) {
127  for (auto &event : event_list) {
128    HWEventData event_data;
129    event_data.event_type = event;
130    event_data_list_.push_back(std::move(event_data));
131  }
132
133  SetEventParser();
134  InitializePollFd();
135}
136
137DisplayError HWEventsDRM::Init(int display_type, HWEventHandler *event_handler,
138                               const vector<HWEvent> &event_list) {
139  if (!event_handler)
140    return kErrorParameters;
141
142  event_handler_ = event_handler;
143  poll_fds_.resize(event_list.size());
144  event_thread_name_ += " - " + std::to_string(display_type);
145
146  PopulateHWEventData(event_list);
147
148  if (pthread_create(&event_thread_, NULL, &DisplayEventThread, this) < 0) {
149    DLOGE("Failed to start %s, error = %s", event_thread_name_.c_str());
150    return kErrorResources;
151  }
152
153  return kErrorNone;
154}
155
156DisplayError HWEventsDRM::Deinit() {
157  exit_threads_ = true;
158  Sys::pthread_cancel_(event_thread_);
159
160  for (uint32_t i = 0; i < event_data_list_.size(); i++) {
161    if (event_data_list_[i].event_type == HWEvent::EXIT) {
162      uint64_t exit_value = 1;
163      ssize_t write_size = Sys::write_(poll_fds_[i].fd, &exit_value, sizeof(uint64_t));
164      if (write_size != sizeof(uint64_t)) {
165        DLOGW("Error triggering exit fd (%d). write size = %d, error = %s", poll_fds_[i].fd,
166              write_size, strerror(errno));
167      }
168    }
169  }
170
171  pthread_join(event_thread_, NULL);
172  CloseFds();
173
174  return kErrorNone;
175}
176
177DisplayError HWEventsDRM::CloseFds() {
178  for (uint32_t i = 0; i < event_data_list_.size(); i++) {
179    switch (event_data_list_[i].event_type) {
180      case HWEvent::VSYNC:
181        poll_fds_[i].fd = -1;
182        break;
183      case HWEvent::EXIT:
184        Sys::close_(poll_fds_[i].fd);
185        poll_fds_[i].fd = -1;
186        break;
187      case HWEvent::IDLE_NOTIFY:
188      case HWEvent::CEC_READ_MESSAGE:
189      case HWEvent::SHOW_BLANK_EVENT:
190      case HWEvent::THERMAL_LEVEL:
191        break;
192      default:
193        return kErrorNotSupported;
194    }
195  }
196
197  return kErrorNone;
198}
199
200void *HWEventsDRM::DisplayEventThread(void *context) {
201  if (context) {
202    return reinterpret_cast<HWEventsDRM *>(context)->DisplayEventHandler();
203  }
204
205  return NULL;
206}
207
208void *HWEventsDRM::DisplayEventHandler() {
209  char data[kMaxStringLength]{};
210
211  prctl(PR_SET_NAME, event_thread_name_.c_str(), 0, 0, 0);
212  setpriority(PRIO_PROCESS, 0, kThreadPriorityUrgent);
213
214  while (!exit_threads_) {
215    if (RegisterVSync() != kErrorNone) {
216      pthread_exit(0);
217      return nullptr;
218    }
219
220    int error = Sys::poll_(poll_fds_.data(), UINT32(poll_fds_.size()), -1);
221    if (error <= 0) {
222      DLOGW("poll failed. error = %s", strerror(errno));
223      continue;
224    }
225
226    for (uint32_t i = 0; i < event_data_list_.size(); i++) {
227      pollfd &poll_fd = poll_fds_[i];
228      switch (event_data_list_[i].event_type) {
229        case HWEvent::VSYNC:
230          (this->*(event_data_list_[i]).event_parser)(nullptr);
231          break;
232        case HWEvent::EXIT:
233          if ((poll_fd.revents & POLLIN) &&
234              (Sys::read_(poll_fd.fd, data, kMaxStringLength) > 0)) {
235            (this->*(event_data_list_[i]).event_parser)(data);
236          }
237          break;
238        case HWEvent::IDLE_NOTIFY:
239        case HWEvent::CEC_READ_MESSAGE:
240        case HWEvent::SHOW_BLANK_EVENT:
241        case HWEvent::THERMAL_LEVEL:
242          if (poll_fd.fd >= 0 && (poll_fd.revents & POLLPRI) &&
243              (Sys::pread_(poll_fd.fd, data, kMaxStringLength, 0) > 0)) {
244            (this->*(event_data_list_[i]).event_parser)(data);
245          }
246          break;
247      }
248    }
249  }
250
251  pthread_exit(0);
252
253  return nullptr;
254}
255
256DisplayError HWEventsDRM::RegisterVSync() {
257  drmVBlank vblank{};
258  vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT);
259  vblank.request.sequence = 1;
260  // DRM hack to pass in context to unused field signal. Driver will write this to the node being
261  // polled on, and will be read as part of drm event handling and sent to handler
262  vblank.request.signal = reinterpret_cast<unsigned long>(this);  // NOLINT
263  int error = drmWaitVBlank(poll_fds_[vsync_index_].fd, &vblank);
264  if (error < 0) {
265    DLOGE("drmWaitVBlank failed with err %d", errno);
266    return kErrorResources;
267  }
268
269  return kErrorNone;
270}
271
272void HWEventsDRM::HandleVSync(char *data) {
273  if (poll_fds_[vsync_index_].revents & (POLLIN | POLLPRI)) {
274    drmEventContext event = {};
275    event.version = DRM_EVENT_CONTEXT_VERSION;
276    event.vblank_handler = &HWEventsDRM::VSyncHandlerCallback;
277    int error = drmHandleEvent(poll_fds_[vsync_index_].fd, &event);
278    if (error != 0) {
279      DLOGE("drmHandleEvent failed: %i", error);
280    }
281  }
282}
283
284void HWEventsDRM::VSyncHandlerCallback(int fd, unsigned int sequence, unsigned int tv_sec,
285                                       unsigned int tv_usec, void *data) {
286  int64_t timestamp = (int64_t)(tv_sec)*1000000000 + (int64_t)(tv_usec)*1000;
287  reinterpret_cast<HWEventsDRM *>(data)->event_handler_->VSync(timestamp);
288}
289
290void HWEventsDRM::HandleIdleTimeout(char *data) {
291  event_handler_->IdleTimeout();
292}
293
294void HWEventsDRM::HandleCECMessage(char *data) {
295  event_handler_->CECMessage(data);
296}
297
298}  // namespace sdm
299