blob: ea60c36a760e18224971fe7aeb176f3273dca99f [file] [log] [blame]
Sean Paule0c4c3d2015-01-20 16:56:04 -05001/*
2 * Copyright (C) 2015 The Android Open Source Project
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#define LOG_TAG "hwcomposer-drm"
18
19#include <fcntl.h>
20#include <errno.h>
21#include <sys/param.h>
Sean Paul9aa5ad32015-01-22 15:47:54 -050022#include <sys/resource.h>
23#include <pthread.h>
Sean Paul9b1bb842015-01-23 01:11:58 -050024#include <queue>
Sean Paule0c4c3d2015-01-20 16:56:04 -050025
26#include <cutils/log.h>
27
28#include <xf86drm.h>
29#include <xf86drmMode.h>
Sean Paule0c4c3d2015-01-20 16:56:04 -050030
31#include <hardware/hardware.h>
32#include <hardware/hwcomposer.h>
33
Sean Paul9aa5ad32015-01-22 15:47:54 -050034#include <sync/sync.h>
Sean Paulf1dc1912015-01-24 01:34:31 -050035#include <sw_sync.h>
Sean Paul9aa5ad32015-01-22 15:47:54 -050036
Sean Paulcd36a9e2015-01-22 18:01:18 -050037#include "drm_hwcomposer.h"
Sean Paule0c4c3d2015-01-20 16:56:04 -050038
39#define ARRAY_SIZE(arr) (int)(sizeof(arr) / sizeof((arr)[0]))
40
41#define HWCOMPOSER_DRM_DEVICE "/dev/dri/card0"
42#define MAX_NUM_DISPLAYS 3
43#define UM_PER_INCH 25400
44
45static const uint32_t panel_types[] = {
46 DRM_MODE_CONNECTOR_LVDS,
47 DRM_MODE_CONNECTOR_eDP,
48 DRM_MODE_CONNECTOR_DSI,
49};
50
Sean Paul9aa5ad32015-01-22 15:47:54 -050051struct hwc_worker {
52 pthread_t thread;
53 pthread_mutex_t lock;
54 pthread_cond_t cond;
55 bool exit;
56};
57
Sean Paule0c4c3d2015-01-20 16:56:04 -050058struct hwc_drm_display {
Sean Paul9aa5ad32015-01-22 15:47:54 -050059 struct hwc_context_t *ctx;
60 int display;
61
Sean Paule0c4c3d2015-01-20 16:56:04 -050062 uint32_t connector_id;
63
64 drmModeModeInfoPtr configs;
65 uint32_t num_configs;
66
67 int active_config;
68 uint32_t active_crtc;
Sean Paul9aa5ad32015-01-22 15:47:54 -050069
70 struct hwc_worker set_worker;
71
Sean Paul9b1bb842015-01-23 01:11:58 -050072 std::queue<struct hwc_drm_bo> buf_queue;
Sean Paul9aa5ad32015-01-22 15:47:54 -050073 struct hwc_drm_bo front;
Sean Paulf1dc1912015-01-24 01:34:31 -050074
75 int timeline_fd;
76 unsigned timeline_next;
Sean Paule0c4c3d2015-01-20 16:56:04 -050077};
78
79struct hwc_context_t {
80 hwc_composer_device_1_t device;
81
82 int fd;
Sean Paule0c4c3d2015-01-20 16:56:04 -050083
84 hwc_procs_t const *procs;
Sean Paulcd36a9e2015-01-22 18:01:18 -050085 struct hwc_import_context *import_ctx;
Sean Paule0c4c3d2015-01-20 16:56:04 -050086
87 struct hwc_drm_display displays[MAX_NUM_DISPLAYS];
88 int num_displays;
89};
90
91static int hwc_get_drm_display(struct hwc_context_t *ctx, int display,
92 struct hwc_drm_display **hd)
93{
94 if (display >= MAX_NUM_DISPLAYS) {
95 ALOGE("Requested display is out-of-bounds %d %d", display,
96 MAX_NUM_DISPLAYS);
97 return -EINVAL;
98 }
99 *hd = &ctx->displays[display];
100 return 0;
101}
102
103static int hwc_prepare_layer(hwc_layer_1_t *layer)
104{
105 /* TODO: We can't handle background right now, defer to sufaceFlinger */
106 if (layer->compositionType == HWC_BACKGROUND) {
107 layer->compositionType = HWC_FRAMEBUFFER;
108 ALOGV("Can't handle background layers yet");
109
110 /* TODO: Support sideband compositions */
111 } else if (layer->compositionType == HWC_SIDEBAND) {
112 layer->compositionType = HWC_FRAMEBUFFER;
113 ALOGV("Can't handle sideband content yet");
114 }
115
116 layer->hints = 0;
117
118 /* TODO: Handle cursor by setting compositionType=HWC_CURSOR_OVERLAY */
119 if (layer->flags & HWC_IS_CURSOR_LAYER) {
120 ALOGV("Can't handle async cursors yet");
121 }
122
123 /* TODO: Handle transformations */
124 if (layer->transform) {
125 ALOGV("Can't handle transformations yet");
126 }
127
128 /* TODO: Handle blending & plane alpha*/
129 if (layer->blending == HWC_BLENDING_PREMULT ||
130 layer->blending == HWC_BLENDING_COVERAGE) {
131 ALOGV("Can't handle blending yet");
132 }
133
134 /* TODO: Handle cropping & scaling */
135
136 return 0;
137}
138
139static int hwc_prepare(hwc_composer_device_1_t */* dev */, size_t num_displays,
140 hwc_display_contents_1_t** display_contents)
141{
142 int ret = 0, i, j;
143
144 /* TODO: Check flags for HWC_GEOMETRY_CHANGED */
145
146 for (i = 0; i < (int)num_displays && i < MAX_NUM_DISPLAYS; i++) {
147 for (j = 0; j < (int)display_contents[i]->numHwLayers; j++) {
148 ret = hwc_prepare_layer(
149 &display_contents[i]->hwLayers[j]);
150 if (ret) {
151 ALOGE("Failed to prepare layer %d:%d", j, i);
152 return ret;
153 }
154 }
155 }
156
157 return ret;
158}
159
Sean Paulcd36a9e2015-01-22 18:01:18 -0500160/*
161 * TODO: This hack allows us to use the importer's fd to drm to add and remove
162 * framebuffers. The reason it exists is because gralloc doesn't export its
163 * bo's, so we have to use its file descriptor to drm for some operations. Once
164 * gralloc behaves, we can remove this.
165 */
166static int hwc_get_fd_for_bo(struct hwc_context_t *ctx, struct hwc_drm_bo *bo)
167{
168 if (bo->importer_fd >= 0)
169 return bo->importer_fd;
170
171 return ctx->fd;
172}
173
Sean Paule0c4c3d2015-01-20 16:56:04 -0500174static bool hwc_mode_is_equal(drmModeModeInfoPtr a, drmModeModeInfoPtr b)
175{
176 return a->clock == b->clock &&
177 a->hdisplay == b->hdisplay &&
178 a->hsync_start == b->hsync_start &&
179 a->hsync_end == b->hsync_end &&
180 a->htotal == b->htotal &&
181 a->hskew == b->hskew &&
182 a->vdisplay == b->vdisplay &&
183 a->vsync_start == b->vsync_start &&
184 a->vsync_end == b->vsync_end &&
185 a->vtotal == b->vtotal &&
186 a->vscan == b->vscan &&
187 a->vrefresh == b->vrefresh &&
188 a->flags == b->flags &&
189 a->type == b->type &&
190 !strcmp(a->name, b->name);
191}
192
Sean Paul9aa5ad32015-01-22 15:47:54 -0500193static int hwc_modeset_required(struct hwc_drm_display *hd,
194 bool *modeset_required)
195{
196 drmModeCrtcPtr crtc;
197 drmModeModeInfoPtr m;
198
199 crtc = drmModeGetCrtc(hd->ctx->fd, hd->active_crtc);
200 if (!crtc) {
201 ALOGE("Failed to get crtc for display %d", hd->display);
202 return -ENODEV;
203 }
204
205 m = &hd->configs[hd->active_config];
206
207 /* Do a modeset if we haven't done one, or the mode has changed */
208 if (!crtc->mode_valid || !hwc_mode_is_equal(m, &crtc->mode))
209 *modeset_required = true;
210 else
211 *modeset_required = false;
212
213 drmModeFreeCrtc(crtc);
214
215 return 0;
216}
217
218static void hwc_flip_handler(int /* fd */, unsigned int /* sequence */,
219 unsigned int /* tv_sec */, unsigned int /* tv_usec */,
220 void */* user_data */)
221{
222}
223
Sean Paul9b1bb842015-01-23 01:11:58 -0500224static int hwc_flip(struct hwc_drm_display *hd, struct hwc_drm_bo *buf)
Sean Paul9aa5ad32015-01-22 15:47:54 -0500225{
226 fd_set fds;
227 drmEventContext event_context;
228 int ret;
229 bool modeset_required;
230
231 ret = hwc_modeset_required(hd, &modeset_required);
232 if (ret) {
233 ALOGE("Failed to determine if modeset is required %d", ret);
234 return ret;
235 }
236 if (modeset_required) {
Sean Paul9b1bb842015-01-23 01:11:58 -0500237 ret = drmModeSetCrtc(hd->ctx->fd, hd->active_crtc, buf->fb_id,
238 0, 0, &hd->connector_id, 1,
Sean Paul9aa5ad32015-01-22 15:47:54 -0500239 &hd->configs[hd->active_config]);
240 if (ret) {
241 ALOGE("Modeset failed for crtc %d",
242 hd->active_crtc);
243 return ret;
244 }
245 return 0;
246 }
247
248 FD_ZERO(&fds);
249 FD_SET(hd->ctx->fd, &fds);
250
251 event_context.version = DRM_EVENT_CONTEXT_VERSION;
252 event_context.page_flip_handler = hwc_flip_handler;
253
Sean Paul9b1bb842015-01-23 01:11:58 -0500254 ret = drmModePageFlip(hd->ctx->fd, hd->active_crtc, buf->fb_id,
Sean Paul9aa5ad32015-01-22 15:47:54 -0500255 DRM_MODE_PAGE_FLIP_EVENT, hd);
256 if (ret) {
257 ALOGE("Failed to flip buffer for crtc %d",
258 hd->active_crtc);
259 return ret;
260 }
261
262 do {
263 ret = select(hd->ctx->fd + 1, &fds, NULL, NULL, NULL);
264 } while (ret == -1 && errno == EINTR);
265
266 if (ret != 1) {
267 ALOGE("Failed waiting for flip to complete\n");
268 return -EINVAL;
269 }
270 drmHandleEvent(hd->ctx->fd, &event_context);
271
272 return 0;
273}
274
Sean Paul3bc48e82015-01-23 01:41:13 -0500275static int hwc_wait_and_set(struct hwc_drm_display *hd,
276 struct hwc_drm_bo *buf)
Sean Paul9aa5ad32015-01-22 15:47:54 -0500277{
278 int ret;
279
Sean Paul3bc48e82015-01-23 01:41:13 -0500280 ret = drmModeAddFB2(hwc_get_fd_for_bo(hd->ctx, buf), buf->width,
281 buf->height, buf->format, buf->gem_handles, buf->pitches,
282 buf->offsets, &buf->fb_id, 0);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500283 if (ret) {
284 ALOGE("could not create drm fb %d", ret);
285 return ret;
286 }
287
Sean Paul3bc48e82015-01-23 01:41:13 -0500288 if (buf->acquire_fence_fd >= 0) {
289 ret = sync_wait(buf->acquire_fence_fd, -1);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500290 if (ret) {
291 ALOGE("Failed to wait for acquire %d", ret);
292 return ret;
293 }
294 }
295
Sean Paul3bc48e82015-01-23 01:41:13 -0500296 ret = hwc_flip(hd, buf);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500297 if (ret) {
298 ALOGE("Failed to perform flip\n");
299 return ret;
300 }
301
302 if (hd->front.fb_id) {
Sean Paulcd36a9e2015-01-22 18:01:18 -0500303 ret = drmModeRmFB(hwc_get_fd_for_bo(hd->ctx, &hd->front),
304 hd->front.fb_id);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500305 if (ret) {
306 ALOGE("Failed to rm fb from front %d", ret);
307 return ret;
308 }
309 }
Sean Paul3bc48e82015-01-23 01:41:13 -0500310 hd->front = *buf;
311
Sean Paul9aa5ad32015-01-22 15:47:54 -0500312 return ret;
313}
314
315static void *hwc_set_worker(void *arg)
316{
317 struct hwc_drm_display *hd = (struct hwc_drm_display *)arg;
318 int ret;
319
320 setpriority(PRIO_PROCESS, 0, HAL_PRIORITY_URGENT_DISPLAY);
321
Sean Paul9aa5ad32015-01-22 15:47:54 -0500322 do {
Sean Paul3bc48e82015-01-23 01:41:13 -0500323 struct hwc_drm_bo buf;
324
325 ret = pthread_mutex_lock(&hd->set_worker.lock);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500326 if (ret) {
Sean Paul3bc48e82015-01-23 01:41:13 -0500327 ALOGE("Failed to lock set lock %d", ret);
328 return NULL;
Sean Paul9aa5ad32015-01-22 15:47:54 -0500329 }
330
Sean Paul3bc48e82015-01-23 01:41:13 -0500331 if (hd->set_worker.exit)
332 goto out;
333
334 if (hd->buf_queue.empty()) {
335 ret = pthread_cond_wait(&hd->set_worker.cond,
336 &hd->set_worker.lock);
337 if (ret) {
338 ALOGE("Failed to wait on condition %d", ret);
339 goto out;
340 }
341 }
342
343 buf = hd->buf_queue.front();
344 hd->buf_queue.pop();
345
346 ret = pthread_mutex_unlock(&hd->set_worker.lock);
347 if (ret) {
348 ALOGE("Failed to unlock set lock %d", ret);
349 return NULL;
350 }
351
352 ret = hwc_wait_and_set(hd, &buf);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500353 if (ret)
354 ALOGE("Failed to wait and set %d", ret);
Sean Paulf1dc1912015-01-24 01:34:31 -0500355
356 ret = sw_sync_timeline_inc(hd->timeline_fd, 1);
357 if (ret)
358 ALOGE("Failed to increment sync timeline %d", ret);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500359 } while (true);
360
Sean Paul3bc48e82015-01-23 01:41:13 -0500361out:
Sean Paul9aa5ad32015-01-22 15:47:54 -0500362 ret = pthread_mutex_unlock(&hd->set_worker.lock);
Sean Paul3bc48e82015-01-23 01:41:13 -0500363 if (ret)
364 ALOGE("Failed to unlock set lock while exiting %d", ret);
Sean Paul9aa5ad32015-01-22 15:47:54 -0500365
366 return NULL;
367}
368
Sean Paule0c4c3d2015-01-20 16:56:04 -0500369static int hwc_set_display(hwc_context_t *ctx, int display,
370 hwc_display_contents_1_t* display_contents)
371{
372 struct hwc_drm_display *hd = NULL;
Sean Paule0c4c3d2015-01-20 16:56:04 -0500373 hwc_layer_1_t *layer = NULL;
Sean Paul9b1bb842015-01-23 01:11:58 -0500374 struct hwc_drm_bo buf;
Sean Paule0c4c3d2015-01-20 16:56:04 -0500375 int ret, i;
Sean Paul9aa5ad32015-01-22 15:47:54 -0500376 uint32_t fb_id;
Sean Paule0c4c3d2015-01-20 16:56:04 -0500377
Sean Paul9b1bb842015-01-23 01:11:58 -0500378 memset(&buf, 0, sizeof(buf));
379
Sean Paule0c4c3d2015-01-20 16:56:04 -0500380 ret = hwc_get_drm_display(ctx, display, &hd);
381 if (ret)
382 return ret;
383
384 if (!hd->active_crtc) {
385 ALOGE("There is no active crtc for display %d", display);
386 return -ENOENT;
387 }
388
389 /*
390 * TODO: We can only support one hw layer atm, so choose either the
391 * first one or the framebuffer target.
392 */
393 if (!display_contents->numHwLayers) {
394 return 0;
395 } else if (display_contents->numHwLayers == 1) {
396 layer = &display_contents->hwLayers[0];
397 } else {
398 for (i = 0; i < (int)display_contents->numHwLayers; i++) {
399 layer = &display_contents->hwLayers[i];
400 if (layer->compositionType == HWC_FRAMEBUFFER_TARGET)
401 break;
402 }
403 if (i == (int)display_contents->numHwLayers) {
404 ALOGE("Could not find a suitable layer for display %d",
405 display);
406 }
407 }
408
Sean Paul3bc48e82015-01-23 01:41:13 -0500409 ret = hwc_create_bo_from_import(ctx->fd, ctx->import_ctx, layer->handle,
410 &buf);
411 if (ret) {
412 ALOGE("Failed to import handle to drm bo %d", ret);
413 return ret;
414 }
415 buf.acquire_fence_fd = layer->acquireFenceFd;
Sean Paul3bc48e82015-01-23 01:41:13 -0500416
Sean Paul9aa5ad32015-01-22 15:47:54 -0500417 ret = pthread_mutex_lock(&hd->set_worker.lock);
Sean Paule0c4c3d2015-01-20 16:56:04 -0500418 if (ret) {
Sean Paul9aa5ad32015-01-22 15:47:54 -0500419 ALOGE("Failed to lock set lock in set() %d", ret);
420 return ret;
421 }
Sean Paulf1dc1912015-01-24 01:34:31 -0500422
423 /*
424 * TODO: Retire and release can use the same sync point here b/c hwc is
425 * restricted to one layer. Once that is no longer true, this will need
426 * to change
427 */
428 hd->timeline_next++;
429 display_contents->retireFenceFd = sw_sync_fence_create(hd->timeline_fd,
430 "drm_hwc_retire", hd->timeline_next);
431 layer->releaseFenceFd = sw_sync_fence_create(hd->timeline_fd,
432 "drm_hwc_release", hd->timeline_next);
Sean Paul9b1bb842015-01-23 01:11:58 -0500433 hd->buf_queue.push(buf);
434
Sean Paul9aa5ad32015-01-22 15:47:54 -0500435 ret = pthread_cond_signal(&hd->set_worker.cond);
436 if (ret) {
437 ALOGE("Failed to signal set worker %d", ret);
438 goto out;
439 }
440
441 ret = pthread_mutex_unlock(&hd->set_worker.lock);
442 if (ret) {
443 ALOGE("Failed to unlock set lock in set() %d", ret);
444 return ret;
445 }
446
447 return ret;
448
Sean Paule0c4c3d2015-01-20 16:56:04 -0500449out:
Sean Paul9aa5ad32015-01-22 15:47:54 -0500450 if (pthread_mutex_unlock(&hd->set_worker.lock))
451 ALOGE("Failed to unlock set lock in set error handler");
452
Sean Paule0c4c3d2015-01-20 16:56:04 -0500453 return ret;
454}
455
456static int hwc_set(hwc_composer_device_1_t *dev, size_t num_displays,
457 hwc_display_contents_1_t** display_contents)
458{
459 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
460 int ret = 0, i;
461
Sean Paule0c4c3d2015-01-20 16:56:04 -0500462 for (i = 0; i < (int)num_displays && i < MAX_NUM_DISPLAYS; i++) {
Sean Paule0c4c3d2015-01-20 16:56:04 -0500463 ret = hwc_set_display(ctx, i, display_contents[i]);
464 }
465
466 return ret;
467}
468
469static int hwc_event_control(struct hwc_composer_device_1 */* dev */,
470 int /* display */, int /* event */, int /* enabled */)
471{
472 int ret;
473
474 /* TODO */
475 return 0;
476}
477
478static int hwc_set_power_mode(struct hwc_composer_device_1* dev, int display,
479 int mode)
480{
481 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
482 struct hwc_drm_display *hd = NULL;
483 drmModeConnectorPtr c;
484 int ret, i;
485 uint32_t dpms_prop = 0;
486 uint64_t dpms_value = 0;
487
488 ret = hwc_get_drm_display(ctx, display, &hd);
489 if (ret)
490 return ret;
491
492 c = drmModeGetConnector(ctx->fd, hd->connector_id);
493 if (!c) {
494 ALOGE("Failed to get connector %d", display);
495 return -ENODEV;
496 }
497
498 for (i = 0; !dpms_prop && i < c->count_props; i++) {
499 drmModePropertyPtr p;
500
501 p = drmModeGetProperty(ctx->fd, c->props[i]);
502 if (!p)
503 continue;
504
505 if (!strcmp(p->name, "DPMS"))
506 dpms_prop = c->props[i];
507
508 drmModeFreeProperty(p);
509 }
510 if (!dpms_prop) {
511 ALOGE("Failed to get DPMS property from display %d", display);
512 ret = -ENOENT;
513 goto out;
514 }
515
516 switch(mode) {
517 case HWC_POWER_MODE_OFF:
518 dpms_value = DRM_MODE_DPMS_OFF;
519 break;
520
521 /* We can't support dozing right now, so go full on */
522 case HWC_POWER_MODE_DOZE:
523 case HWC_POWER_MODE_DOZE_SUSPEND:
524 case HWC_POWER_MODE_NORMAL:
525 dpms_value = DRM_MODE_DPMS_ON;
526 break;
527 };
528
529 ret = drmModeConnectorSetProperty(ctx->fd, c->connector_id,
530 dpms_prop, dpms_value);
531 if (ret) {
532 ALOGE("Failed to set DPMS property for display %d", display);
533 goto out;
534 }
535
536out:
537 drmModeFreeConnector(c);
538 return ret;
539}
540
541static int hwc_query(struct hwc_composer_device_1 */* dev */, int what,
542 int *value)
543{
544 switch(what) {
545 case HWC_BACKGROUND_LAYER_SUPPORTED:
546 *value = 0; /* TODO: We should do this */
547 break;
548 case HWC_VSYNC_PERIOD:
549 ALOGW("Query for deprecated vsync value, returning 60Hz");
550 *value = 1000 * 1000 * 1000 / 60;
551 break;
552 case HWC_DISPLAY_TYPES_SUPPORTED:
553 *value = HWC_DISPLAY_PRIMARY | HWC_DISPLAY_EXTERNAL;
554 break;
555 }
556 return 0;
557}
558
559static void hwc_register_procs(struct hwc_composer_device_1* dev,
560 hwc_procs_t const* procs)
561{
562 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
563
564 ctx->procs = procs;
565}
566
567static int hwc_get_display_configs(struct hwc_composer_device_1* dev,
568 int display, uint32_t* configs, size_t* numConfigs)
569{
570 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
571 struct hwc_drm_display *hd = NULL;
572 drmModeConnectorPtr c;
573 int ret = 0, i;
574
575 if (!*numConfigs)
576 return 0;
577
578 ret = hwc_get_drm_display(ctx, display, &hd);
579 if (ret)
580 return ret;
581
582 c = drmModeGetConnector(ctx->fd, hd->connector_id);
583 if (!c) {
584 ALOGE("Failed to get connector %d", display);
585 return -ENODEV;
586 }
587
588 if (hd->configs)
589 free(hd->configs);
590
591 hd->active_config = -1;
592 hd->configs = (drmModeModeInfoPtr)calloc(c->count_modes,
593 sizeof(*hd->configs));
594 if (!hd->configs) {
595 ALOGE("Failed to allocate config list for display %d", display);
596 ret = -ENOMEM;
597 hd->num_configs = 0;
598 goto out;
599 }
600
601 for (i = 0; i < c->count_modes; i++) {
602 drmModeModeInfoPtr m = &hd->configs[i];
603
604 memcpy(m, &c->modes[i], sizeof(*m));
605
606 if (i < (int)*numConfigs)
607 configs[i] = i;
608 }
609
610 hd->num_configs = c->count_modes;
611 *numConfigs = MIN(c->count_modes, *numConfigs);
612
613out:
614 drmModeFreeConnector(c);
615 return ret;
616}
617
618static int hwc_check_config_valid(struct hwc_context_t *ctx,
619 drmModeConnectorPtr connector, int display,
620 int config_idx)
621{
622 struct hwc_drm_display *hd = NULL;
623 drmModeModeInfoPtr m = NULL;
624 int ret = 0, i;
625
626 ret = hwc_get_drm_display(ctx, display, &hd);
627 if (ret)
628 return ret;
629
630 /* Make sure the requested config is still valid for the display */
631 for (i = 0; i < connector->count_modes; i++) {
632 if (hwc_mode_is_equal(&connector->modes[i],
633 &hd->configs[config_idx])) {
634 m = &hd->configs[config_idx];
635 break;
636 }
637 }
638 if (!m)
639 return -ENOENT;
640
641 return 0;
642}
643
644static int hwc_get_display_attributes(struct hwc_composer_device_1* dev,
645 int display, uint32_t config, const uint32_t* attributes,
646 int32_t* values)
647{
648 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
649 struct hwc_drm_display *hd = NULL;
650 drmModeConnectorPtr c;
651 drmModeModeInfoPtr m;
652 int ret, i;
653
654 ret = hwc_get_drm_display(ctx, display, &hd);
655 if (ret)
656 return ret;
657
658 if (config >= hd->num_configs) {
659 ALOGE("Requested config is out-of-bounds %d %d", config,
660 hd->num_configs);
661 return -EINVAL;
662 }
663
664 c = drmModeGetConnector(ctx->fd, hd->connector_id);
665 if (!c) {
666 ALOGE("Failed to get connector %d", display);
667 return -ENODEV;
668 }
669
670 ret = hwc_check_config_valid(ctx, c, display, (int)config);
671 if (ret) {
672 ALOGE("Provided config is no longer valid %u", config);
673 goto out;
674 }
675
676 m = &hd->configs[config];
677 for (i = 0; attributes[i] != HWC_DISPLAY_NO_ATTRIBUTE; i++) {
678 switch(attributes[i]) {
679 case HWC_DISPLAY_VSYNC_PERIOD:
680 values[i] = 1000 * 1000 * 1000 / m->vrefresh;
681 break;
682 case HWC_DISPLAY_WIDTH:
683 values[i] = m->hdisplay;
684 break;
685 case HWC_DISPLAY_HEIGHT:
686 values[i] = m->vdisplay;
687 break;
688 case HWC_DISPLAY_DPI_X:
689 /* Dots per 1000 inches */
690 values[i] = c->mmWidth ?
691 (m->hdisplay * UM_PER_INCH) / c->mmWidth : 0;
692 break;
693 case HWC_DISPLAY_DPI_Y:
694 /* Dots per 1000 inches */
695 values[i] = c->mmHeight ?
696 (m->vdisplay * UM_PER_INCH) / c->mmHeight : 0;
697 break;
698 }
699 }
700
701out:
702 drmModeFreeConnector(c);
703 return ret;
704}
705
706static int hwc_get_active_config(struct hwc_composer_device_1* dev, int display)
707{
708 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
709 struct hwc_drm_display *hd = NULL;
710 drmModeConnectorPtr c;
711 int ret;
712
713 ret = hwc_get_drm_display(ctx, display, &hd);
714 if (ret)
715 return ret;
716
717 if (hd->active_config < 0)
718 return -1;
719
720 c = drmModeGetConnector(ctx->fd, hd->connector_id);
721 if (!c) {
722 ALOGE("Failed to get connector %d", display);
723 return -ENODEV;
724 }
725
726 ret = hwc_check_config_valid(ctx, c, display, hd->active_config);
727 if (ret) {
728 ALOGE("Config is no longer valid %d", hd->active_config);
729 ret = -1;
730 goto out;
731 }
732
733 ret = hd->active_config;
734
735out:
736 drmModeFreeConnector(c);
737 return ret;
738}
739
740static bool hwc_crtc_is_bound(struct hwc_context_t *ctx, uint32_t crtc_id)
741{
742 int i;
743
744 for (i = 0; i < MAX_NUM_DISPLAYS; i++) {
745 if (ctx->displays[i].active_crtc == crtc_id)
746 return true;
747 }
748 return false;
749}
750
751static int hwc_try_encoder(struct hwc_context_t *ctx, drmModeResPtr r,
752 uint32_t encoder_id, uint32_t *crtc_id)
753{
754 drmModeEncoderPtr e;
755 int ret, i;
756
757 e = drmModeGetEncoder(ctx->fd, encoder_id);
758 if (!e) {
759 ALOGE("Failed to get encoder for connector %d", encoder_id);
760 return -ENODEV;
761 }
762
763 /* First try to use the currently-bound crtc */
764 if (e->crtc_id) {
765 if (!hwc_crtc_is_bound(ctx, e->crtc_id)) {
766 *crtc_id = e->crtc_id;
767 ret = 0;
768 goto out;
769 }
770 }
771
772 /* Try to find a possible crtc which will work */
773 for (i = 0; i < r->count_crtcs; i++) {
774 if (!(e->possible_crtcs & (1 << i)))
775 continue;
776
777 /* We've already tried this earlier */
778 if (e->crtc_id == r->crtcs[i])
779 continue;
780
781 if (!hwc_crtc_is_bound(ctx, r->crtcs[i])) {
782 *crtc_id = r->crtcs[i];
783 ret = 0;
784 goto out;
785 }
786 }
787
788 /* We can't use the encoder, but nothing went wrong, try another one */
789 ret = -EAGAIN;
790
791out:
792 drmModeFreeEncoder(e);
793 return ret;
794}
795
796static int hwc_set_active_config(struct hwc_composer_device_1* dev, int display,
797 int index)
798{
799 struct hwc_context_t *ctx = (struct hwc_context_t *)&dev->common;
800 struct hwc_drm_display *hd = NULL;
801 drmModeResPtr r = NULL;
802 drmModeConnectorPtr c;
803 uint32_t crtc_id = 0;
804 int ret, i;
805 bool new_crtc, new_encoder;
806
807 ret = hwc_get_drm_display(ctx, display, &hd);
808 if (ret)
809 return ret;
810
811 c = drmModeGetConnector(ctx->fd, hd->connector_id);
812 if (!c) {
813 ALOGE("Failed to get connector %d", display);
814 return -ENODEV;
815 }
816
817 if (c->connection == DRM_MODE_DISCONNECTED) {
818 ALOGE("Tried to configure a disconnected display %d", display);
819 ret = -ENODEV;
820 goto out;
821 }
822
823 ret = hwc_check_config_valid(ctx, c, display, index);
824 if (ret) {
825 ALOGE("Provided config is no longer valid %u", index);
826 ret = -ENOENT;
827 goto out;
828 }
829
830 r = drmModeGetResources(ctx->fd);
831 if (!r) {
832 ALOGE("Failed to get drm resources");
833 goto out;
834 }
835
836 /* We no longer have an active_crtc */
837 hd->active_crtc = 0;
838
839 /* First, try to use the currently-connected encoder */
840 if (c->encoder_id) {
841 ret = hwc_try_encoder(ctx, r, c->encoder_id, &crtc_id);
842 if (ret && ret != -EAGAIN) {
843 ALOGE("Encoder try failed %d", ret);
844 goto out;
845 }
846 }
847
848 /* We couldn't find a crtc with the attached encoder, try the others */
849 if (!crtc_id) {
850 for (i = 0; i < c->count_encoders; i++) {
851 ret = hwc_try_encoder(ctx, r, c->encoders[i], &crtc_id);
852 if (!ret) {
853 break;
854 } else if (ret != -EAGAIN) {
855 ALOGE("Encoder try failed %d", ret);
856 goto out;
857 }
858 }
859 if (!crtc_id) {
860 ALOGE("Couldn't find valid crtc to modeset");
861 ret = -EINVAL;
862 goto out;
863 }
864 }
865
866 hd->active_crtc = crtc_id;
867 hd->active_config = index;
868
869 /* TODO: Once we have atomic, set the crtc timing info here */
870
871out:
872 if (r)
873 drmModeFreeResources(r);
874
875 drmModeFreeConnector(c);
876 return ret;
877}
878
Sean Paul9aa5ad32015-01-22 15:47:54 -0500879static int hwc_destroy_worker(struct hwc_worker *worker)
880{
881 int ret;
882
883 ret = pthread_mutex_lock(&worker->lock);
884 if (ret) {
885 ALOGE("Failed to lock in destroy() %d", ret);
886 return ret;
887 }
888
889 worker->exit = true;
890
891 ret |= pthread_cond_signal(&worker->cond);
892 if (ret)
893 ALOGE("Failed to signal cond in destroy() %d", ret);
894
895 ret |= pthread_mutex_unlock(&worker->lock);
896 if (ret)
897 ALOGE("Failed to unlock in destroy() %d", ret);
898
899 ret |= pthread_join(worker->thread, NULL);
900 if (ret && ret != ESRCH)
901 ALOGE("Failed to join thread in destroy() %d", ret);
902
903 return ret;
904}
905
906static void hwc_destroy_display(struct hwc_drm_display *hd)
907{
908 int ret;
909
910 if (hwc_destroy_worker(&hd->set_worker))
911 ALOGE("Destroy set worker failed");
912}
913
Sean Paule0c4c3d2015-01-20 16:56:04 -0500914static int hwc_device_close(struct hw_device_t *dev)
915{
916 struct hwc_context_t *ctx = (struct hwc_context_t *)dev;
Sean Paulcd36a9e2015-01-22 18:01:18 -0500917 int ret, i;
Sean Paule0c4c3d2015-01-20 16:56:04 -0500918
Sean Paul9aa5ad32015-01-22 15:47:54 -0500919 for (i = 0; i < MAX_NUM_DISPLAYS; i++)
920 hwc_destroy_display(&ctx->displays[i]);
921
922 drmClose(ctx->fd);
Sean Paulcd36a9e2015-01-22 18:01:18 -0500923
924 ret = hwc_import_destroy(ctx->import_ctx);
925 if (ret)
926 ALOGE("Could not destroy import %d", ret);
927
Sean Paule0c4c3d2015-01-20 16:56:04 -0500928 free(ctx);
929
930 return 0;
931}
932
Sean Paul9aa5ad32015-01-22 15:47:54 -0500933static int hwc_initialize_worker(struct hwc_drm_display *hd,
934 struct hwc_worker *worker, void *(*routine)(void*))
935{
936 int ret;
937
938 ret = pthread_cond_init(&worker->cond, NULL);
939 if (ret) {
940 ALOGE("Failed to create worker condition %d", ret);
941 return ret;
942 }
943
944 ret = pthread_mutex_init(&worker->lock, NULL);
945 if (ret) {
946 ALOGE("Failed to initialize worker lock %d", ret);
947 goto err_cond;
948 }
949
950 worker->exit = false;
951
952 ret = pthread_create(&worker->thread, NULL, routine, hd);
953 if (ret) {
954 ALOGE("Could not create worker thread %d", ret);
955 goto err_lock;
956 }
957 return 0;
958
959err_lock:
960 pthread_mutex_destroy(&worker->lock);
961err_cond:
962 pthread_cond_destroy(&worker->cond);
963 return ret;
964}
965
Sean Paule0c4c3d2015-01-20 16:56:04 -0500966static int hwc_initialize_display(struct hwc_context_t *ctx, int display,
967 uint32_t connector_id)
968{
969 struct hwc_drm_display *hd = NULL;
970 int ret;
971
972 ret = hwc_get_drm_display(ctx, display, &hd);
973 if (ret)
974 return ret;
975
Sean Paul9aa5ad32015-01-22 15:47:54 -0500976 hd->ctx = ctx;
977 hd->display = display;
Sean Paule0c4c3d2015-01-20 16:56:04 -0500978 hd->active_config = -1;
979 hd->connector_id = connector_id;
980
Sean Paulf1dc1912015-01-24 01:34:31 -0500981 ret = sw_sync_timeline_create();
982 if (ret < 0) {
983 ALOGE("Failed to create sw sync timeline %d", ret);
984 return ret;
985 }
986 hd->timeline_fd = ret;
987
Sean Paul9aa5ad32015-01-22 15:47:54 -0500988 ret = hwc_initialize_worker(hd, &hd->set_worker, hwc_set_worker);
989 if (ret) {
990 ALOGE("Failed to create set worker %d\n", ret);
991 return ret;
992 }
993
Sean Paule0c4c3d2015-01-20 16:56:04 -0500994 return 0;
995}
996
997static int hwc_enumerate_displays(struct hwc_context_t *ctx)
998{
999 struct hwc_drm_display *panel_hd;
1000 drmModeResPtr res;
1001 drmModeConnectorPtr *conn_list;
1002 int ret = 0, i, j;
1003
1004 res = drmModeGetResources(ctx->fd);
1005 if (!res) {
1006 ALOGE("Failed to get drm resources");
1007 return -ENODEV;
1008 }
1009
1010 conn_list = (drmModeConnector **)calloc(res->count_connectors,
1011 sizeof(*conn_list));
1012 if (!conn_list) {
1013 ALOGE("Failed to allocate connector list");
1014 ret = -ENOMEM;
1015 goto out;
1016 }
1017
1018 for (i = 0; i < res->count_connectors; i++) {
1019 conn_list[i] = drmModeGetConnector(ctx->fd, res->connectors[i]);
1020 if (!conn_list[i]) {
1021 ALOGE("Failed to get connector %d", res->connectors[i]);
1022 ret = -ENODEV;
1023 goto out;
1024 }
1025 }
1026
1027 ctx->num_displays = 0;
1028
1029 /* Find a connected, panel type connector for display 0 */
1030 for (i = 0; i < res->count_connectors; i++) {
1031 drmModeConnectorPtr c = conn_list[i];
1032
1033 for (j = 0; j < ARRAY_SIZE(panel_types); j++) {
1034 if (c->connector_type == panel_types[j] &&
1035 c->connection == DRM_MODE_CONNECTED)
1036 break;
1037 }
1038 if (j == ARRAY_SIZE(panel_types))
1039 continue;
1040
1041 hwc_initialize_display(ctx, ctx->num_displays, c->connector_id);
1042 ctx->num_displays++;
1043 break;
1044 }
1045
1046 ret = hwc_get_drm_display(ctx, 0, &panel_hd);
1047 if (ret)
1048 goto out;
1049
1050 /* Fill in the other displays */
1051 for (i = 0; i < res->count_connectors; i++) {
1052 drmModeConnectorPtr c = conn_list[i];
1053
1054 if (panel_hd->connector_id == c->connector_id)
1055 continue;
1056
1057 hwc_initialize_display(ctx, ctx->num_displays, c->connector_id);
1058 ctx->num_displays++;
1059 }
1060
1061out:
1062 for (i = 0; i < res->count_connectors; i++) {
1063 if (conn_list[i])
1064 drmModeFreeConnector(conn_list[i]);
1065 }
1066 free(conn_list);
1067
1068 if (res)
1069 drmModeFreeResources(res);
1070
1071 return ret;
1072}
1073
1074static int hwc_device_open(const struct hw_module_t* module, const char* name,
1075 struct hw_device_t** dev)
1076{
1077 int ret = 0;
1078 struct hwc_context_t *ctx;
1079
1080 if (strcmp(name, HWC_HARDWARE_COMPOSER)) {
1081 ALOGE("Invalid module name- %s", name);
1082 return -EINVAL;
1083 }
1084
Sean Paul9b1bb842015-01-23 01:11:58 -05001085 ctx = new hwc_context_t();
Sean Paule0c4c3d2015-01-20 16:56:04 -05001086 if (!ctx) {
1087 ALOGE("Failed to allocate hwc context");
1088 return -ENOMEM;
1089 }
1090
Sean Paulcd36a9e2015-01-22 18:01:18 -05001091 ret = hwc_import_init(&ctx->import_ctx);
Sean Paule0c4c3d2015-01-20 16:56:04 -05001092 if (ret) {
Sean Paulcd36a9e2015-01-22 18:01:18 -05001093 ALOGE("Failed to initialize import context");
Sean Paule0c4c3d2015-01-20 16:56:04 -05001094 goto out;
1095 }
1096
1097 /* TODO: Use drmOpenControl here instead */
1098 ctx->fd = open(HWCOMPOSER_DRM_DEVICE, O_RDWR);
1099 if (ctx->fd < 0) {
1100 ALOGE("Failed to open dri- %s", strerror(-errno));
1101 goto out;
1102 }
1103
1104 ret = drmSetMaster(ctx->fd);
1105 if (ret) {
1106 ALOGE("Failed to set hwcomposer as drm master %d", ret);
1107 goto out;
1108 }
1109
1110 ret = hwc_enumerate_displays(ctx);
1111 if (ret) {
1112 ALOGE("Failed to enumerate displays: %s", strerror(ret));
1113 goto out;
1114 }
1115
1116 ctx->device.common.tag = HARDWARE_DEVICE_TAG;
1117 ctx->device.common.version = HWC_DEVICE_API_VERSION_1_4;
1118 ctx->device.common.module = const_cast<hw_module_t*>(module);
1119 ctx->device.common.close = hwc_device_close;
1120
1121 ctx->device.prepare = hwc_prepare;
1122 ctx->device.set = hwc_set;
1123 ctx->device.eventControl = hwc_event_control;
1124 ctx->device.setPowerMode = hwc_set_power_mode;
1125 ctx->device.query = hwc_query;
1126 ctx->device.registerProcs = hwc_register_procs;
1127 ctx->device.getDisplayConfigs = hwc_get_display_configs;
1128 ctx->device.getDisplayAttributes = hwc_get_display_attributes;
1129 ctx->device.getActiveConfig = hwc_get_active_config;
1130 ctx->device.setActiveConfig = hwc_set_active_config;
1131 ctx->device.setCursorPositionAsync = NULL; /* TODO: Add cursor */
1132
1133 *dev = &ctx->device.common;
1134
1135 return 0;
1136out:
1137 if (ctx->fd >= 0)
1138 close(ctx->fd);
1139
1140 free(ctx);
1141 return ret;
1142}
1143
1144static struct hw_module_methods_t hwc_module_methods = {
1145 open: hwc_device_open
1146};
1147
1148hwc_module_t HAL_MODULE_INFO_SYM = {
1149 common: {
1150 tag: HARDWARE_MODULE_TAG,
1151 version_major: 1,
1152 version_minor: 0,
1153 id: HWC_HARDWARE_MODULE_ID,
1154 name: "DRM hwcomposer module",
1155 author: "The Android Open Source Project",
1156 methods: &hwc_module_methods,
1157 dso: NULL,
1158 reserved: { 0 },
1159 }
1160};