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