blob: 6ed3817399ef4facc1c6be9aa859cf1f66ef677e [file] [log] [blame]
Ao Xu6747bbd2020-09-28 20:02:09 +08001/*
2 * Copyright (C) 2020 Amlogic, Inc. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 */
9
10#include <stdio.h>
11#include <fcntl.h>
12#include <sys/types.h>
13#include <sys/stat.h>
14#include <stddef.h>
15#include <drm_fourcc.h>
16#include "meson_drm_util.h"
17
18#define MAX_OSD_PLANE 6
19#define MAX_VIDEO_PLANE 4
20#define MAX_CONNECTOR 3
21#define MAX_CRTC 3
22
23struct kms_display;
24
25#define VOID2U64(x) ((uint64_t)(unsigned long)(x))
26#define container_of(ptr, type, member) \
27 (type *)((char *)(ptr) - (char *) &((type *)0)->member)
28
29#define to_kms_display(x) container_of(x, struct kms_display, base)
30
31struct property {
32 uint32_t id;
33 uint64_t value;
34};
35
36struct crtc_state {
37 uint32_t id;
38
39 struct property mode_id;
40 struct property active;
41 struct property out_fence;
42 struct property video_out_fence;
43};
44
45struct connector_state {
46 uint32_t id;
47 uint32_t crtc_mask;
48 drmModeModeInfo mode;
49
50 struct property crtc_id;
51};
52
53struct plane_state {
54 uint32_t id;
55 uint32_t crtc_mask;
56
57 struct property crtc_id;
58 struct property crtc_x;
59 struct property crtc_y;
60 struct property crtc_w;
61 struct property crtc_h;
62 struct property fb_id;
63 struct property src_x;
64 struct property src_y;
65 struct property src_w;
66 struct property src_h;
67
68 struct property type;
69 struct property in_fence_fd;
70 struct property in_formats;
71};
72
73struct plane {
74 drmModePlane *plane;
75 drmModeObjectProperties *props;
76 drmModePropertyRes **props_info;
77};
78
79struct crtc {
80 drmModeCrtc *crtc;
81 drmModeObjectProperties *props;
82 drmModePropertyRes **props_info;
83};
84
85struct connector {
86 drmModeConnector *connector;
87 drmModeObjectProperties *props;
88 drmModePropertyRes **props_info;
89};
90
91struct kms_display {
92 struct drm_display base;
93
94 struct plane *plane;
95 struct crtc *crtc;
96 struct connector *conn;
97
98 int num_crtc;
99 struct crtc_state *crtc_states[MAX_CRTC];
100 int num_connector;
101 struct connector_state *conn_states[MAX_CONNECTOR];
102 int num_osd_plane;
103 struct plane_state *osd_states[MAX_OSD_PLANE];
104 int num_vid_plane;
105 struct plane_state *vid_states[MAX_OSD_PLANE];
106
107 int mode_set;
108};
109
110static void kms_destroy_display(struct drm_display *drm_disp)
111{
112 struct kms_display *disp = to_kms_display(drm_disp);
113 free(disp->conn);
114 free(disp->crtc);
115 free(disp->plane);
116 free(disp);
117}
118
119static int alloc_bos(struct drm_display *drm_disp, struct drm_buf *buf, uint32_t *bo_handles)
120{
121 int i;
122 uint32_t size = 0;
123 uint32_t width = buf->width;
124 uint32_t height = buf->height;
125
126 for (i = 0; i < buf->nbo; i++) {
127 if (buf->nbo == 1 && buf->flags == MESON_USE_VIDEO_AFBC) {
128 size = width * height;
129 buf->pitches[i] = width * 2;
130 buf->commit_to_video = 1;
131 } else if (buf->flags | MESON_USE_VIDEO_PLANE) {
132 if ( i == 0 )
133 size = width * height;
134 else
135 size = width * height / 2;
136
137 buf->pitches[i] = width;
138 buf->commit_to_video = 1;
139 }
140
141 buf->size += size;
142 buf->bos[i] = meson_bo_create(drm_disp->dev, size, buf->flags);
143 if (buf->bos[i]) {
144 bo_handles[i] = meson_bo_handle(buf->bos[i]);
145 buf->fd[i] = meson_bo_dmabuf(buf->bos[i]);
146 } else {
147 fprintf(stderr, "meson_bo_create fila\n");
148 return -1;
149 }
150 }
151
152 return 0;
153}
154
155static int add_framebuffer(int fd, struct drm_buf *buf, uint32_t *bo_handles, uint64_t modifier)
156{
157 int i, ret;
158 uint64_t modifiers[4] = { 0, 0, 0, 0 };
159 uint32_t flags = 0;
160 uint32_t fb_id;
161
162 for ( i = 0; i < buf->nbo; i++) {
163 if (bo_handles[i] != 0 && modifier != DRM_FORMAT_MOD_NONE) {
164 flags |= DRM_MODE_FB_MODIFIERS;
165 modifiers[i] = modifier;
166 }
167 }
168
169 ret = drmModeAddFB2WithModifiers(fd, buf->width, buf->height, buf->fourcc,
170 bo_handles, buf->pitches, buf->offsets, modifiers, &fb_id, flags);
171 if (ret < 0) {
172 fprintf(stderr, "Unable to add framebuffer for plane: %s\n", strerror(errno));
173 return -1;
174 }
175
176 buf->fb_id = fb_id;
177 return 0;
178}
179
180static struct drm_buf *kms_alloc_buf(struct drm_display *drm_disp, struct drm_buf_metadata *info)
181{
182 struct drm_buf *buf = NULL;
183 uint32_t bo_handles[4] = {0};
184
185 buf = calloc(1, sizeof(*buf));
186 buf->fourcc = info->fourcc;
187 buf->width = info->width;
188 buf->height = info->height;
189 buf->flags = info->flags;
190 buf->fence_fd = -1;
191 buf->disp = drm_disp;
192
193 if (!info->fourcc)
194 buf->fourcc = DRM_FORMAT_ARGB8888;
195
196 switch (buf->fourcc) {
197 case DRM_FORMAT_ARGB8888:
198 buf->nbo = 1;
199 break;
200 case DRM_FORMAT_UYVY:
201 case DRM_FORMAT_YUYV:
202 buf->nbo = 1;
203 break;
204 case DRM_FORMAT_NV12:
205 case DRM_FORMAT_NV21:
206 buf->nbo = 2;
207 break;
208 default:
209 fprintf(stderr, "unsupport format: 0x%08x\n", buf->fourcc);
210 break;
211 }
212
213 alloc_bos(drm_disp, buf, bo_handles);
214
215 add_framebuffer(drm_disp->drm_fd, buf, bo_handles, DRM_FORMAT_MOD_NONE);
216
217 return buf;
218}
219static int kms_alloc_bufs(struct drm_display *drm_disp, int num, struct drm_buf_metadata *info)
220{
221 int i;
222 struct drm_buf *buf;
223 uint32_t bo_handles[4] = {0};
224
225 drm_disp->bufs = calloc(num, sizeof(*drm_disp->bufs));
226 drm_disp->nbuf = num;
227
228 for ( i = 0; i < num; i++) {
229 buf = &drm_disp->bufs[i];
230 buf->fourcc = info->fourcc;
231 buf->width = info->width;
232 buf->height = info->height;
233 buf->flags = info->flags;
234 buf->fence_fd = -1;
235 buf->disp = drm_disp;
236
237 if (!info->fourcc)
238 buf->fourcc = DRM_FORMAT_ARGB8888;
239
240 switch (buf->fourcc) {
241 case DRM_FORMAT_ARGB8888:
242 buf->nbo = 1;
243 break;
244 case DRM_FORMAT_UYVY:
245 case DRM_FORMAT_YUYV:
246 buf->nbo = 1;
247 break;
248 case DRM_FORMAT_NV12:
249 case DRM_FORMAT_NV21:
250 buf->nbo = 2;
251 break;
252 default:
253 fprintf(stderr, "unsupport format: 0x%08x\n", buf->fourcc);
254 break;
255 }
256
257 alloc_bos(drm_disp, buf, bo_handles);
258
259 add_framebuffer(drm_disp->drm_fd, buf, bo_handles, DRM_FORMAT_MOD_NONE);
260 }
261 return 0;
262}
263
264static int free_buf(struct drm_display *drm_disp, struct drm_buf *buf)
265{
266 int i, ret;
267 struct drm_mode_destroy_dumb destroy_dumb;
268
269 for ( i = 0; i < buf->nbo; i++)
270 close(buf->fd[i]);
271
272 drmModeRmFB(drm_disp->drm_fd, buf->fb_id);
273 memset(&destroy_dumb, 0, sizeof(destroy_dumb));
274
275 for ( i = 0; i < buf->nbo; i++) {
276 destroy_dumb.handle = meson_bo_handle(buf->bos[i]);
277
278 ret = drmIoctl(drm_disp->drm_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb);
279 if (ret < 0) {
280 /* If handle was from drmPrimeFDToHandle, then fd is connected
281 * as render, we have to use drm_gem_close to release it.
282 */
283 if (errno == EACCES) {
284 struct drm_gem_close close_req;
285 close_req.handle = destroy_dumb.handle;
286 ret = drmIoctl(drm_disp->drm_fd, DRM_IOCTL_GEM_CLOSE, &close_req);
287 if (ret < 0) {
288 fprintf(stderr, "Unable to destroy buffer: %s\n",
289 strerror(errno));
290 return -1;
291 }
292 }
293 }
294 }
295
296 return 0;
297}
298static int kms_free_bufs(struct drm_display *drm_disp)
299{
300 int i;
301 struct drm_buf *buf;
302
303 for ( i = 0; i < drm_disp->nbuf; i++ ) {
304 buf = &drm_disp->bufs[i];
305 free_buf(drm_disp, buf);
306 }
307
308 return 0;
309}
310
311static int kms_free_buf(struct drm_buf *buf)
312{
313 free_buf(buf->disp, buf);
314 return 0;
315}
316
317static int kms_post_buf(struct drm_display *drm_disp, struct drm_buf *buf)
318{
319 int ret;
320 drmModeAtomicReqPtr request;
321 uint32_t blob_id;
322 uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK;
323 struct kms_display *disp = to_kms_display(drm_disp);
324
325 struct plane_state *plane_state;
326 struct crtc_state *crtc_state;
327 struct connector_state *conn_state;
328
329 conn_state = disp->conn_states[0];
330 crtc_state = disp->crtc_states[0];
331
332 if (buf->flags | MESON_USE_VD1)
333 plane_state = disp->vid_states[0];
334 else if (buf->flags | MESON_USE_VD2)
335 plane_state = disp->vid_states[1];
336 else
337 plane_state = disp->osd_states[0];
338
339 request = drmModeAtomicAlloc();
340
341 if (!disp->mode_set) {
342
343 flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
344 drmModeAtomicAddProperty(request, conn_state->id, conn_state->crtc_id.id, crtc_state->id);
345
346 if (drmModeCreatePropertyBlob(drm_disp->drm_fd, &disp->conn_states[0]->mode,
347 sizeof(drmModeModeInfo), &blob_id) != 0 ) {
348 return -1;
349 }
350 drmModeAtomicAddProperty(request, crtc_state->id, crtc_state->mode_id.id, blob_id);
351 drmModeAtomicAddProperty(request, crtc_state->id, crtc_state->active.id, 1);
352
353 disp->mode_set = 1;
354 }
355
356 drmModeAtomicAddProperty(request, plane_state->id, plane_state->crtc_x.id, 0);
357 drmModeAtomicAddProperty(request, plane_state->id, plane_state->crtc_y.id, 0);
358 drmModeAtomicAddProperty(request, plane_state->id, plane_state->crtc_w.id, conn_state->mode.hdisplay);
359 drmModeAtomicAddProperty(request, plane_state->id, plane_state->crtc_h.id, conn_state->mode.vdisplay);
360 drmModeAtomicAddProperty(request, plane_state->id, plane_state->src_x.id, 0);
361 drmModeAtomicAddProperty(request, plane_state->id, plane_state->src_y.id, 0);
362 drmModeAtomicAddProperty(request, plane_state->id, plane_state->src_w.id, buf->width << 16);
363 drmModeAtomicAddProperty(request, plane_state->id, plane_state->src_h.id, buf->height << 16);
364 drmModeAtomicAddProperty(request, plane_state->id, plane_state->fb_id.id, buf->fb_id);
365 drmModeAtomicAddProperty(request, plane_state->id, plane_state->crtc_id.id, crtc_state->id);
366#if 0
367 if (buf->flags | (MESON_USE_VD2 | MESON_USE_VD1))
368 drmModeAtomicAddProperty(request, crtc_state->id, crtc_state->video_out_fence.id, VOID2U64(&buf->fence_fd));
369 else
370 drmModeAtomicAddProperty(request, crtc_state->id, crtc_state->out_fence.id, VOID2U64(&buf->fence_fd));
371#endif
372 ret = drmModeAtomicCommit(drm_disp->drm_fd, request, flags, NULL);
373 if (ret < 0) {
374 fprintf(stderr, "Unable to flip page: %s\n", strerror(errno));
375 goto error;
376 }
377
378 ret = 0;
379 goto complete;
380
381error:
382 ret = -1;
383complete:
384 drmModeAtomicFree(request);
385
386 return ret;
387}
388
389static void getproperty(int drm_fd, drmModeObjectProperties* props, const char *name,
390 struct property *p)
391{
392 int i;
393 drmModePropertyRes *res;
394 for (i = 0; i < props->count_props; i++) {
395 res = drmModeGetProperty(drm_fd, props->props[i]);
396 if (strcmp(name, res->name))
397 continue;
398
399 p->id = res->prop_id;
400 p->value = props->prop_values[i];
401 //fprintf(stdout, "getproperty: %s, id: %u, value: %llu \n", res->name, p->id, p->value);
402 }
403}
404
405static int populate_connectors(drmModeRes *resources, struct kms_display *disp)
406{
407 int ret, i, j;
408 int num_connector = 0;
409 struct connector_state *state;
410 drmModeConnector *connector;
411 drmModeEncoder *encoder;
412 drmModeObjectProperties *props;
413
414 for (i = 0; i < resources->count_connectors; i++) {
415 connector = drmModeGetConnector(disp->base.drm_fd, resources->connectors[i]);
416 disp->conn[i].connector = connector;
417
418 if ( connector->connector_type == DRM_MODE_CONNECTOR_TV )
419 continue;
420
421 state = calloc(1, sizeof(*state));
422 disp->conn_states[num_connector++] = state;
423 state->id = resources->connectors[i];
424
425 for (j = 0; j < connector->count_encoders; j++) {
426 encoder = drmModeGetEncoder(disp->base.drm_fd, connector->encoders[j]);
427 state->crtc_mask |= encoder->possible_crtcs;
428 }
429
430 if (connector->count_modes)
431 state->mode = connector->modes[0];
432
433 for (j = 0; j < connector->count_modes; j++) {
434 if (connector->modes[j].type & DRM_MODE_TYPE_PREFERRED) {
435 state->mode = connector->modes[j];
436 break;
437 }
438 }
439
440 disp->base.width = state->mode.hdisplay;
441 disp->base.height = state->mode.vdisplay;
442 disp->base.vrefresh = state->mode.vrefresh;
443
444 props = drmModeObjectGetProperties(disp->base.drm_fd, resources->connectors[i], DRM_MODE_OBJECT_CONNECTOR);
445 disp->conn[i].props = props;
446 getproperty(disp->base.drm_fd, props, "CRTC_ID", &state->crtc_id);
447 }
448
449 disp->num_connector = num_connector;
450 fprintf(stdout, "found %d connector\n", num_connector);
451}
452
453static int populate_crtcs(drmModeRes *resources, struct kms_display *disp)
454{
455 int ret, i;
456 int num_crtc = 0;
457 struct crtc_state *state;
458 drmModeObjectProperties *props;
459
460 for (i = 0; i < resources->count_crtcs; i++) {
461 state = calloc(1, sizeof(*state));
462 disp->crtc_states[num_crtc++] = state;
463 state->id = resources->crtcs[i];
464
465 props = drmModeObjectGetProperties(disp->base.drm_fd, resources->crtcs[i], DRM_MODE_OBJECT_CRTC);
466 disp->crtc[i].props = props;
467 getproperty(disp->base.drm_fd, props, "MODE_ID", &state->mode_id);
468 getproperty(disp->base.drm_fd, props, "ACTIVE", &state->active);
469 getproperty(disp->base.drm_fd, props, "OUT_FENCE_PTR", &state->out_fence);
470 getproperty(disp->base.drm_fd, props, "VIDEO_OUT_FENCE_PTR", &state->video_out_fence);
471 }
472
473 disp->num_crtc = num_crtc;
474
475 fprintf(stdout, "found %d crtc\n", num_crtc);
476}
477
478static int is_plane_support_yuv(drmModePlane * plane)
479{
480 int i;
481 for (i = 0; i < plane->count_formats; i++) {
482 switch (plane->formats[i]) {
483 case DRM_FORMAT_NV12:
484 case DRM_FORMAT_NV21:
485 return 1;
486 default:
487 return 0;
488 }
489 }
490
491 return 0;
492}
493
494static int populate_planes(drmModeRes *resources, struct kms_display *disp)
495{
496 int ret, i;
497 int num_osd_plane = 0;
498 int num_vid_plane = 0;
499 struct plane_state *state;
500 drmModePlane *plane;
501 drmModePlaneRes *plane_res;
502 drmModeObjectProperties *props;
503
504 plane_res = drmModeGetPlaneResources(disp->base.drm_fd);
505
506 disp->plane = calloc(plane_res->count_planes, sizeof(*disp->plane));
507
508 for (i = 0; i < plane_res->count_planes; i++) {
509 state = calloc(1, sizeof(*state));
510 plane = drmModeGetPlane(disp->base.drm_fd, plane_res->planes[i]);
511 disp->plane[i].plane = plane;
512 if (is_plane_support_yuv(plane))
513 disp->vid_states[num_vid_plane++] = state;
514 else
515 disp->osd_states[num_osd_plane++] = state;
516
517 state->id = plane_res->planes[i];
518 state->crtc_mask = plane->possible_crtcs;
519
520 props = drmModeObjectGetProperties(disp->base.drm_fd, plane_res->planes[i], DRM_MODE_OBJECT_PLANE);
521 disp->plane[i].props = props;
522 getproperty(disp->base.drm_fd, props, "CRTC_ID", &state->crtc_id);
523 getproperty(disp->base.drm_fd, props, "CRTC_X", &state->crtc_x);
524 getproperty(disp->base.drm_fd, props, "CRTC_Y", &state->crtc_y);
525 getproperty(disp->base.drm_fd, props, "CRTC_W", &state->crtc_w);
526 getproperty(disp->base.drm_fd, props, "CRTC_H", &state->crtc_h);
527 getproperty(disp->base.drm_fd, props, "FB_ID", &state->fb_id);
528 getproperty(disp->base.drm_fd, props, "SRC_X", &state->src_x);
529 getproperty(disp->base.drm_fd, props, "SRC_Y", &state->src_y);
530 getproperty(disp->base.drm_fd, props, "SRC_W", &state->src_w);
531 getproperty(disp->base.drm_fd, props, "SRC_H", &state->src_h);
532 getproperty(disp->base.drm_fd, props, "type", &state->type);
533 getproperty(disp->base.drm_fd, props, "IN_FENCE_FD", &state->in_fence_fd);
534 getproperty(disp->base.drm_fd, props, "IN_FORMATS", &state->in_formats);
535 }
536
537 disp->num_osd_plane = num_osd_plane;
538 disp->num_vid_plane = num_vid_plane;
539
540 fprintf(stdout, "found %d osd, %d video\n", num_osd_plane, num_vid_plane);
541}
542
543static int drm_kms_init_resource(struct kms_display *disp)
544{
545 int ret;
546 int drm_fd;
547 drmModeRes *resources;
548
549 drm_fd = open("/dev/dri/card0", O_RDWR | O_CLOEXEC);
550 if (drm_fd < 0) {
551 fprintf(stderr, "Unable to open DRM node: %s\n",
552 strerror(errno));
553 return -1;
554 }
555
556 disp->base.drm_fd = drm_fd;
557 disp->base.dev = meson_device_create(drm_fd);
558
559 ret = drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1);
560 if (ret < 0) {
561 fprintf(stderr, "Unable to set DRM atomic capability: %s\n",
562 strerror(errno));
563 return -1;
564 }
565
566 ret = drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
567 if (ret < 0) {
568 fprintf(stderr, "Unable to set DRM universal planes capability: %s\n",
569 strerror(errno));
570 return -1;
571 }
572
573 resources = drmModeGetResources(drm_fd);
574 if (!resources) {
575 fprintf(stderr, "drmModeGetResources failed: %s\n", strerror(errno));
576 return -1;
577 }
578
579 disp->conn = calloc(resources->count_connectors, sizeof(*disp->conn));
580 disp->crtc = calloc(resources->count_crtcs, sizeof(*disp->crtc));
581
582 populate_connectors(resources, disp);
583 populate_crtcs(resources, disp);
584 populate_planes(resources, disp);
585
586 return 0;
587}
588
589struct drm_display *drm_kms_init(void)
590{
591 struct kms_display *display;
592 struct drm_display *base;
593 display = calloc(1, sizeof(*display));
594
595 base = &display->base;
596 base->destroy_display = kms_destroy_display;
597 base->alloc_bufs = kms_alloc_bufs;
598 base->free_bufs = kms_free_bufs;
599
600 base->alloc_buf = kms_alloc_buf;
601 base->free_buf = kms_free_buf;
602 base->post_buf = kms_post_buf;
603
604 drm_kms_init_resource(display);
605
606 return base;
607}