1 /*
2 * Copyright (C) Texas Instruments Incorporated - http://www.ti.com/
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <algorithm>
18
19 #include <cstdint>
20
21 #include <log/log.h>
22 #include <cutils/properties.h>
23
24 #include "display.h"
25 #include "format.h"
26 #include "hwc_dev.h"
27
HWCDisplay(enum disp_role role)28 HWCDisplay::HWCDisplay(enum disp_role role) :
29 active_config(0),
30 role(role),
31 cb_procs(NULL),
32 is_dummy(false),
33 vsync_on(false),
34 blanked(true),
35 is_flip_pending(false)
36 {
37 }
38
setup_composition_pipes()39 void HWCDisplay::setup_composition_pipes()
40 {
41 std::unique_lock<std::mutex> lock(this->mutex);
42
43 KMSDisplay* kdisp = &this->disp_link;
44
45 int count = 0;
46 for (auto plane : kdisp->card->get_planes()) {
47 auto possible_crtcs = plane->get_possible_crtcs();
48 if (std::find(possible_crtcs.begin(), possible_crtcs.end(), kdisp->crtc) != possible_crtcs.end()) {
49 planeProps[count].plane = plane;
50 ALOGI("Overlay %d: plane_id: %d", count, planeProps[count].plane->id());
51 count++;
52 }
53 }
54 }
55
56 /* Page flip handler callback */
page_flip_handler(int fd,unsigned int frame,unsigned int sec,unsigned int usec,void * data)57 void HWCDisplay::page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data)
58 {
59 HWCDisplay* display = static_cast<HWCDisplay*>(data);
60
61 std::unique_lock<std::mutex> lock(display->mutex);
62
63 if (display->is_flip_pending == false) {
64 ALOGW("Spurious page flip?");
65 return;
66 }
67
68 /* release the old buffers */
69 for (auto current_fb_info : display->current_fb_infos)
70 delete current_fb_info;
71 display->current_fb_infos.clear();
72
73 /* pending are now current */
74 for (auto pending_fb_info : display->pending_fb_infos)
75 display->current_fb_infos.push_back(pending_fb_info);
76 display->pending_fb_infos.clear();
77
78 display->is_flip_pending = false;
79 lock.unlock();
80 display->cond_flip.notify_one();
81 }
82
vblank_kick(HWCDisplay * display)83 static int vblank_kick(HWCDisplay* display)
84 {
85 drmVBlank vblank;
86 memset(&vblank, 0, sizeof(vblank));
87 vblank.request.type = (drmVBlankSeqType)(DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT |
88 display->role == DISP_ROLE_SECONDARY ? DRM_VBLANK_SECONDARY : 0);
89 vblank.request.sequence = 1;
90 vblank.request.signal = (unsigned long)display;
91 int err = drmWaitVBlank(display->disp_link.card->fd(), &vblank);
92 if (err < 0) {
93 /* FIXME: error in drmWaitVBlank() use SW vsync instead? */
94 ALOGE("drmWaitVBlank error %d (%s)", err, strerror(errno));
95 return -1;
96 }
97
98 return 0;
99 }
100
101 /* Callback function that gets triggered on vsync */
vblank_handler(int fd,unsigned int frame,unsigned int sec,unsigned int usec,void * data)102 void HWCDisplay::vblank_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, void* data)
103 {
104 HWCDisplay* display = static_cast<HWCDisplay*>(data);
105
106 std::unique_lock<std::mutex> lock(display->mutex);
107
108 if (display->vsync_on) {
109 int64_t ts = sec * (int64_t)1000000000 + usec * (int64_t)1000;
110
111 // ALOGD("Sending VBLANK at %lld for display %d", ts, display->role);
112 display->cb_procs->vsync(display->cb_procs, display->role, ts);
113
114 vblank_kick(display);
115 }
116 }
117
set_vsync_state(bool state)118 int HWCDisplay::set_vsync_state(bool state)
119 {
120 std::unique_lock<std::mutex> lock(this->mutex);
121
122 if (this->is_dummy)
123 return 0;
124
125 this->vsync_on = state;
126
127 if (this->vsync_on)
128 return vblank_kick(this);
129
130 return 0;
131 }
132
set_plane_properties(kms::AtomicReq & req,drm_plane_props_t * plane_props)133 static void set_plane_properties(kms::AtomicReq& req, drm_plane_props_t* plane_props)
134 {
135 kms::Plane* plane = plane_props->plane;
136
137 req.add(plane, "FB_ID", plane_props->fb_info->fb_id);
138
139 req.add(plane, "IN_FENCE_FD", plane_props->layer->acquireFenceFd);
140
141 req.add(plane, {
142 { "CRTC_ID", plane_props->crtc_id },
143 { "SRC_X", (plane_props->src_x) << 16 },
144 { "SRC_Y", (plane_props->src_y) << 16 },
145 { "SRC_W", (plane_props->src_w) << 16 },
146 { "SRC_H", (plane_props->src_h) << 16 },
147 { "CRTC_X", plane_props->crtc_x },
148 { "CRTC_Y", plane_props->crtc_y },
149 { "CRTC_W", plane_props->crtc_w },
150 { "CRTC_H", plane_props->crtc_h },
151 });
152 }
153
update_display(drm_plane_props_t * planeProp)154 int HWCDisplay::update_display(drm_plane_props_t* planeProp)
155 {
156 std::unique_lock<std::mutex> lock(this->mutex);
157
158 this->cond_flip.wait(lock, [this]{return !this->is_flip_pending;});
159
160 buffer_handle_t handle = planeProp->layer->handle;
161 if (!handle) {
162 ALOGW("Got empty handle, nothing to display");
163 return 0;
164 }
165
166 KMSDisplay* kdisp = &this->disp_link;
167
168 planeProp->fb_info = new DRMFramebuffer(kdisp->card->fd(), handle, false);
169 this->pending_fb_infos.push_back(planeProp->fb_info);
170
171 planeProp->crtc_id = kdisp->crtc->id();
172
173 // Atomic display update
174 kms::AtomicReq req(*kdisp->card);
175 set_plane_properties(req, planeProp);
176 int ret = req.commit(this, true);
177 if (ret) {
178 ALOGE("cannot do atomic commit/page flip: %d (%s)", ret, strerror(errno));
179 for (auto pending_fb_info : this->pending_fb_infos)
180 delete pending_fb_info;
181 this->pending_fb_infos.clear();
182 return ret;
183 }
184
185 // ALOGD("Scheduled page flip on display %d", this->role);
186 this->is_flip_pending = true;
187
188 return 0;
189 }
190
blank(int blank)191 void HWCDisplay::blank(int blank)
192 {
193 std::unique_lock<std::mutex> lock(this->mutex);
194
195 if (this->is_dummy)
196 return;
197
198 KMSDisplay* kdisp = &this->disp_link;
199
200 ALOGI("Linking connector %d to crtc %d", kdisp->con->id(), kdisp->crtc->id());
201
202 int ret = kdisp->crtc->set_mode(kdisp->con, kdisp->mode);
203 if (ret) {
204 ALOGE("Failed to set crtc mode (%s)", strerror(-ret));
205 return;
206 }
207
208 // FIXME: This should actually blank the screen
209 this->blanked = blank;
210 }
211
get_display_configs(uint32_t * configs,size_t * numConfigs)212 int HWCDisplay::get_display_configs(uint32_t* configs, size_t* numConfigs)
213 {
214 if (!configs || !numConfigs)
215 return -EINVAL;
216
217 if (*numConfigs == 0)
218 return 0;
219
220 std::unique_lock<std::mutex> lock(this->mutex);
221
222 size_t num = this->configs.size();
223 if (num > *numConfigs)
224 num = *numConfigs;
225
226 for (size_t i = 0; i < num; i++)
227 configs[i] = i;
228
229 *numConfigs = num;
230
231 return 0;
232 }
233
get_display_attributes(uint32_t cfg,const uint32_t * attributes,int32_t * values)234 int HWCDisplay::get_display_attributes(uint32_t cfg, const uint32_t* attributes, int32_t* values)
235 {
236 if (!attributes || !values)
237 return 0;
238
239 std::unique_lock<std::mutex> lock(this->mutex);
240
241 if (cfg >= this->configs.size())
242 return -EINVAL;
243
244 display_config_t* config = &this->configs[cfg];
245
246 for (size_t i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
247 switch (attributes[i]) {
248 case HWC_DISPLAY_VSYNC_PERIOD:
249 values[i] = 1000000000 / config->fps;
250 break;
251 case HWC_DISPLAY_WIDTH:
252 values[i] = config->xres;
253 break;
254 case HWC_DISPLAY_HEIGHT:
255 values[i] = config->yres;
256 break;
257 case HWC_DISPLAY_DPI_X:
258 values[i] = 1000 * config->xdpi;
259 break;
260 case HWC_DISPLAY_DPI_Y:
261 values[i] = 1000 * config->ydpi;
262 break;
263 }
264 }
265
266 return 0;
267 }
268