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