| /* |
| * Copyright (c) 2019 Amlogic, Inc. All rights reserved. |
| * |
| * This source code is subject to the terms and conditions defined in the |
| * file 'LICENSE' which is part of this source code package. |
| * |
| * Description: |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <pthread.h> |
| #include <string.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/ioctl.h> |
| #include <sys/mman.h> |
| #include <sys/stat.h> |
| |
| #include <xf86drm.h> |
| #include <xf86drmMode.h> |
| #include <libsync.h> |
| #include <drm_fourcc.h> |
| #include <linux/videodev2.h> |
| #include <meson_drm.h> |
| |
| #include "aml_avsync.h" |
| #include "aml_avsync_log.h" |
| #include "drm.h" |
| #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) |
| #define MAX_BUF_SIZE 32 |
| |
| static char* drm_master_dev_name = "/dev/dri/card0"; |
| static char* drm_cli_dev_name = "/dev/dri/renderD128"; |
| static int drm_fd, drm_cli_fd; |
| static int drm_mode_set; |
| static int secure_mode; |
| extern unsigned int global_plane_id; |
| extern char mode_str[16]; |
| extern unsigned int vfresh; |
| extern int log_level; |
| |
| struct gem_buffer { |
| uint32_t width; |
| uint32_t height; |
| unsigned int planes_count; |
| unsigned int size; |
| unsigned int handles[4]; |
| unsigned int pitches[4]; |
| unsigned int offsets[4]; |
| int export_fds[4]; |
| |
| unsigned int framebuffer_id; |
| }; |
| |
| struct display_properties_ids { |
| uint32_t connector_crtc_id; |
| uint32_t crtc_mode_id; |
| uint32_t crtc_active; |
| uint32_t plane_fb_id; |
| uint32_t plane_crtc_id; |
| uint32_t plane_src_x; |
| uint32_t plane_src_y; |
| uint32_t plane_src_w; |
| uint32_t plane_src_h; |
| uint32_t plane_crtc_x; |
| uint32_t plane_crtc_y; |
| uint32_t plane_crtc_w; |
| uint32_t plane_crtc_h; |
| uint32_t plane_zpos; |
| }; |
| |
| struct plane { |
| drmModePlane *plane; |
| drmModeObjectProperties *props; |
| drmModePropertyRes **props_info; |
| }; |
| |
| struct crtc { |
| drmModeCrtc *crtc; |
| drmModeObjectProperties *props; |
| drmModePropertyRes **props_info; |
| }; |
| |
| struct connector { |
| drmModeConnector *connector; |
| drmModeObjectProperties *props; |
| drmModePropertyRes **props_info; |
| }; |
| |
| struct display_setup { |
| unsigned int connector_id; |
| unsigned int encoder_id; |
| unsigned int crtc_id; |
| unsigned int plane_id; |
| |
| unsigned int x; |
| unsigned int y; |
| unsigned int scaled_width; |
| unsigned int scaled_height; |
| unsigned int crtc_width; |
| unsigned int crtc_height; |
| |
| struct display_properties_ids properties_ids; |
| drmModeModeInfo mode; |
| |
| struct plane *plane; |
| struct crtc *crtc; |
| struct connector *connector; |
| } setup; |
| |
| struct aml_display { |
| bool started; |
| pthread_t disp_t; |
| #if 0 |
| unsigned int q_len; |
| unsigned int ri; //read index |
| unsigned int wi; //write index |
| unsigned int total_num; |
| struct drm_frame *queue; |
| #endif |
| void * avsync; |
| bool last_frame; |
| }; |
| |
| static struct gem_buffer *gem_buf; |
| static struct gem_buffer osd_gem_buf; |
| static displayed_cb_func display_cb; |
| static struct aml_display aml_dis; |
| |
| #define TSYNC_MODE "/sys/class/tsync/mode" |
| #define TSYNC_PCRSCR "/sys/class/tsync/pts_pcrscr" |
| #define VPTS_INC_UPINT "/sys/class/video/vsync_pts_inc_upint" |
| |
| static int create_meson_gem_buffer(int fd, enum frame_format fmt, |
| struct gem_buffer *buffer) |
| { |
| struct drm_meson_gem_create gem_create; |
| int rc; |
| int i; |
| int width = buffer->width; |
| int height = buffer->height; |
| |
| if (!buffer) |
| return -1; |
| |
| for (i = 0; i < buffer->planes_count; i++) { |
| memset(&gem_create, 0, sizeof(gem_create)); |
| |
| if (fmt == FRAME_FMT_AFBC) { |
| gem_create.flags = MESON_USE_VIDEO_AFBC; |
| gem_create.size = width * height * 2; |
| buffer->pitches[i] = width*2; |
| } else { |
| gem_create.flags = MESON_USE_VIDEO_PLANE; |
| if (secure_mode) |
| gem_create.flags |= MESON_USE_PROTECTED; |
| if (i == 0) |
| gem_create.size = width * height; |
| else |
| gem_create.size = width * height / 2; |
| buffer->pitches[i] = width; |
| } |
| |
| rc = drmIoctl(fd, DRM_IOCTL_MESON_GEM_CREATE, &gem_create); |
| if (rc < 0) { |
| printf("Unable to create dumb buffer: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| buffer->size += gem_create.size; |
| buffer->handles[i] = gem_create.handle; |
| buffer->offsets[i] = 0; |
| |
| rc = drmPrimeHandleToFD(fd, buffer->handles[i], DRM_CLOEXEC | DRM_RDWR, |
| &buffer->export_fds[i]); |
| if (rc < 0) { |
| fprintf(stderr, "drmPirmeHandleToFD fail: %s\n", strerror(errno)); |
| return -1; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int close_buffer(int fd, struct gem_buffer *buffer) |
| { |
| int rc; |
| int i; |
| struct drm_mode_destroy_dumb destroy_dumb; |
| |
| if (!buffer) |
| return -1; |
| |
| for (i = 0; i < buffer->planes_count; i++) |
| close(buffer->export_fds[i]); |
| |
| drmModeRmFB(fd, buffer->framebuffer_id); |
| |
| memset(&destroy_dumb, 0, sizeof(destroy_dumb)); |
| for (i = 0; i < buffer->planes_count; i++) { |
| destroy_dumb.handle = buffer->handles[i]; |
| |
| rc = drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_dumb); |
| if (rc < 0) { |
| /* If handle was from drmPrimeFDToHandle, then fd is connected |
| * as render, we have to use drm_gem_close to release it. |
| */ |
| if (errno == EACCES) { |
| struct drm_gem_close close_req; |
| close_req.handle = destroy_dumb.handle; |
| rc = drmIoctl(fd, DRM_IOCTL_GEM_CLOSE, &close_req); |
| if (rc < 0) { |
| fprintf(stderr, "Unable to destroy buffer: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int add_framebuffer(int fd, struct gem_buffer *buffer, |
| enum frame_format fmt, uint64_t modifier) |
| { |
| uint64_t modifiers[4] = { 0, 0, 0, 0 }; |
| uint32_t flags = 0; |
| unsigned int id; |
| unsigned int i; |
| int rc; |
| uint32_t drm_fmt; |
| |
| if (fmt == FRAME_FMT_NV21) |
| drm_fmt = DRM_FORMAT_NV21; |
| else if (fmt == FRAME_FMT_NV12) |
| drm_fmt = DRM_FORMAT_NV12; |
| else if (fmt == FRAME_FMT_AFBC) |
| drm_fmt = DRM_FORMAT_YUYV; |
| else { |
| printf("ftm %d not supported\n", fmt); |
| return -1; |
| } |
| |
| for (i = 0; i < buffer->planes_count; i++) { |
| if (buffer->handles[i] != 0 && |
| modifier != DRM_FORMAT_MOD_NONE) { |
| flags |= DRM_MODE_FB_MODIFIERS; |
| modifiers[i] = modifier; |
| } |
| } |
| |
| rc = drmModeAddFB2WithModifiers(fd, buffer->width, |
| buffer->height, drm_fmt, |
| buffer->handles, buffer->pitches, |
| buffer->offsets, modifiers, &id, flags); |
| if (rc < 0) { |
| fprintf(stderr, "Unable to add framebuffer for plane: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| buffer->framebuffer_id = id; |
| |
| return 0; |
| } |
| |
| static int discover_properties(int fd, struct display_properties_ids *ids) |
| { |
| //int connector_id = setup.connector_id; |
| int plane_id = setup.plane_id; |
| drmModeObjectPropertiesPtr properties = NULL; |
| drmModePropertyPtr property = NULL; |
| struct { |
| uint32_t object_type; |
| uint32_t object_id; |
| char *name; |
| uint32_t *value; |
| } glue[] = { |
| { DRM_MODE_OBJECT_PLANE, plane_id, "FB_ID", &ids->plane_fb_id }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_ID", &ids->plane_crtc_id }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "SRC_X", &ids->plane_src_x }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "SRC_Y", &ids->plane_src_y }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "SRC_W", &ids->plane_src_w }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "SRC_H", &ids->plane_src_h }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_X", &ids->plane_crtc_x }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_Y", &ids->plane_crtc_y }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_W", &ids->plane_crtc_w }, |
| { DRM_MODE_OBJECT_PLANE, plane_id, "CRTC_H", &ids->plane_crtc_h }, |
| }; |
| unsigned int i, j; |
| int rc; |
| |
| for (i = 0; i < ARRAY_SIZE(glue); i++) { |
| properties = drmModeObjectGetProperties(fd, |
| glue[i].object_id, |
| glue[i].object_type); |
| if (properties == NULL) { |
| fprintf(stderr, "Unable to get DRM properties(%s): %s\n", |
| glue[i].name, strerror(errno)); |
| goto error; |
| } |
| |
| for (j = 0; j < properties->count_props; j++) { |
| property = drmModeGetProperty(fd, |
| properties->props[j]); |
| if (property == NULL) { |
| fprintf(stderr, "Unable to get DRM property: %s\n", |
| strerror(errno)); |
| goto error; |
| } |
| |
| if (strcmp(property->name, glue[i].name) == 0) { |
| *glue[i].value = property->prop_id; |
| break; |
| } |
| |
| drmModeFreeProperty(property); |
| property = NULL; |
| } |
| |
| if (j == properties->count_props) { |
| fprintf(stderr, "Unable to find property for %s\n", |
| glue[i].name); |
| goto error; |
| } |
| |
| drmModeFreeProperty(property); |
| property = NULL; |
| |
| drmModeFreeObjectProperties(properties); |
| properties = NULL; |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| rc = -1; |
| |
| complete: |
| if (property != NULL) |
| drmModeFreeProperty(property); |
| |
| if (properties != NULL) |
| drmModeFreeObjectProperties(properties); |
| |
| return rc; |
| } |
| |
| static int add_connector_property(drmModeAtomicReq *req, uint32_t obj_id, |
| const char *name, uint64_t value, struct display_setup *setup) |
| { |
| struct connector *obj = setup->connector; |
| unsigned int i; |
| int prop_id = 0; |
| |
| for (i = 0 ; i < obj->props->count_props ; i++) { |
| if (strcmp(obj->props_info[i]->name, name) == 0) { |
| prop_id = obj->props_info[i]->prop_id; |
| break; |
| } |
| } |
| |
| if (prop_id < 0) { |
| printf("no connector property: %s\n", name); |
| return -EINVAL; |
| } |
| |
| return drmModeAtomicAddProperty(req, obj_id, prop_id, value); |
| } |
| |
| static int add_crtc_property(drmModeAtomicReq *req, uint32_t obj_id, |
| const char *name, uint64_t value, struct display_setup *setup) |
| { |
| struct crtc *obj = setup->crtc; |
| unsigned int i; |
| int prop_id = -1; |
| |
| for (i = 0 ; i < obj->props->count_props ; i++) { |
| if (strcmp(obj->props_info[i]->name, name) == 0) { |
| prop_id = obj->props_info[i]->prop_id; |
| break; |
| } |
| } |
| |
| if (prop_id < 0) { |
| printf("no crtc property: %s\n", name); |
| return -EINVAL; |
| } |
| |
| return drmModeAtomicAddProperty(req, obj_id, prop_id, value); |
| } |
| |
| int span(struct timeval* t1, struct timeval* t2) |
| { |
| int64_t time = (t2->tv_sec - t1->tv_sec); |
| time *= 1000000; |
| time += t2->tv_usec - t1->tv_usec; |
| time /= 1000; |
| return (int)time; |
| } |
| |
| static int page_flip(int fd, unsigned int crtc_id, unsigned int plane_id, |
| struct display_properties_ids *ids, |
| struct gem_buffer* gem_buf) |
| { |
| drmModeAtomicReqPtr request; |
| uint32_t flags = DRM_MODE_ATOMIC_NONBLOCK; |
| int rc; |
| //int out_fence_fd = 0; |
| //struct timeval t1, t2; |
| |
| request = drmModeAtomicAlloc(); |
| |
| if (!drm_mode_set) { |
| unsigned int connector_id = setup.connector_id; |
| uint32_t blob_id; |
| |
| flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; |
| if (add_connector_property(request, connector_id, "CRTC_ID", |
| crtc_id, &setup) < 0) |
| return -1; |
| |
| if (drmModeCreatePropertyBlob(fd, &setup.mode, sizeof(drmModeModeInfo), |
| &blob_id) != 0) |
| return -1; |
| |
| if (add_crtc_property(request, crtc_id, "MODE_ID", blob_id, &setup) < 0) |
| return -1; |
| |
| if (add_crtc_property(request, crtc_id, "ACTIVE", 1, &setup) < 0) |
| return -1; |
| |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_x, 0); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_y, 0); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_w, |
| setup.scaled_width); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_h, |
| setup.scaled_height); |
| drm_mode_set = 1; |
| } |
| |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_src_x, 0); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_src_y, 0); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_src_w, |
| gem_buf->width << 16); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_src_h, |
| gem_buf->height << 16); |
| |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_fb_id, |
| gem_buf->framebuffer_id); |
| drmModeAtomicAddProperty(request, plane_id, ids->plane_crtc_id, |
| crtc_id); |
| |
| rc = drmModeAtomicCommit(fd, request, flags, NULL); |
| if (rc < 0) { |
| fprintf(stderr, "Unable to flip page: %s\n", strerror(errno)); |
| goto error; |
| } |
| |
| rc = 0; |
| goto complete; |
| |
| error: |
| rc = -1; |
| |
| complete: |
| drmModeAtomicFree(request); |
| |
| return rc; |
| } |
| |
| static uint32_t find_crtc_for_encoder(const drmModeRes *resources, |
| const drmModeEncoder *encoder) { |
| int i; |
| |
| for (i = 0; i < resources->count_crtcs; i++) { |
| /* possible_crtcs is a bitmask as described here: |
| * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api |
| */ |
| const uint32_t crtc_mask = 1 << i; |
| const uint32_t crtc_id = resources->crtcs[i]; |
| if (encoder->possible_crtcs & crtc_mask) { |
| return crtc_id; |
| } |
| } |
| |
| /* no match found */ |
| return -1; |
| } |
| |
| static uint32_t find_crtc_for_connector(int fd, const drmModeRes *resources, |
| const drmModeConnector *connector) { |
| int i; |
| |
| for (i = 0; i < connector->count_encoders; i++) { |
| const uint32_t encoder_id = connector->encoders[i]; |
| drmModeEncoder *encoder = drmModeGetEncoder(fd, encoder_id); |
| |
| if (encoder) { |
| const uint32_t crtc_id = find_crtc_for_encoder(resources, encoder); |
| |
| drmModeFreeEncoder(encoder); |
| if (crtc_id != 0) { |
| return crtc_id; |
| } |
| } |
| } |
| |
| /* no match found */ |
| return -1; |
| } |
| |
| static int init_drm(int fd, drmModeModeInfo *mode) |
| { |
| drmModeRes *resources; |
| drmModeConnector *connector = NULL; |
| drmModeEncoder *encoder = NULL; |
| int i, area; |
| drmModeModeInfo *curr_mode = NULL; |
| |
| |
| resources = drmModeGetResources(fd); |
| if (!resources) { |
| printf("drmModeGetResources failed: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| /* find a connected connector: */ |
| for (i = 0; i < resources->count_connectors; i++) { |
| connector = drmModeGetConnector(fd, resources->connectors[i]); |
| if (connector->connection == DRM_MODE_CONNECTED) { |
| /* it's connected, let's use this! */ |
| break; |
| } |
| drmModeFreeConnector(connector); |
| connector = NULL; |
| } |
| |
| if (!connector) { |
| /* we could be fancy and listen for hotplug events and wait for |
| * a connector.. |
| */ |
| printf("no connected connector!\n"); |
| return -1; |
| } |
| |
| setup.connector_id = connector->connector_id; |
| |
| /* find preferred mode or the highest resolution mode: */ |
| |
| if (*mode_str) { |
| for (i = 0; i < connector->count_modes; i++) { |
| drmModeModeInfo *current_mode = &connector->modes[i]; |
| |
| if (current_mode->name && strcmp(current_mode->name, mode_str) == 0) { |
| if (vfresh == 0 || current_mode->vrefresh == vfresh) { |
| curr_mode = current_mode; |
| printf("found the request mode: %s-%d.\n", current_mode->name, |
| current_mode->vrefresh); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!curr_mode) { |
| printf("requested mode not found, using default mode!\n"); |
| for (i = 0, area = 0; i < connector->count_modes; i++) { |
| drmModeModeInfo *current_mode = &connector->modes[i]; |
| |
| if (current_mode->type & DRM_MODE_TYPE_PREFERRED) { |
| curr_mode = current_mode; |
| break; |
| } |
| |
| int current_area = current_mode->hdisplay * current_mode->vdisplay; |
| if (current_area > area) { |
| curr_mode = current_mode; |
| area = current_area; |
| } |
| } |
| } |
| |
| if (!curr_mode) { |
| printf("could not find mode!\n"); |
| return -1; |
| } |
| |
| memcpy(mode, curr_mode, sizeof(drmModeModeInfo)); |
| |
| /* find encoder: */ |
| for (i = 0; i < resources->count_encoders; i++) { |
| encoder = drmModeGetEncoder(fd, resources->encoders[i]); |
| if (encoder->encoder_id == connector->encoder_id) |
| break; |
| drmModeFreeEncoder(encoder); |
| encoder = NULL; |
| } |
| |
| if (encoder) { |
| setup.crtc_id = encoder->crtc_id; |
| } else { |
| uint32_t crtc_id1 = find_crtc_for_connector(fd, resources, connector); |
| setup.crtc_id = crtc_id1; |
| } |
| |
| drmModeFreeResources(resources); |
| |
| return 0; |
| } |
| |
| static int OsdBufferCreate(int fd, uint32_t width, |
| uint32_t height, struct gem_buffer *buffer) { |
| int rc; |
| uint32_t flags = 0; |
| void *data; |
| struct drm_mode_create_dumb create_dumb; |
| uint64_t modifiers[4] = { 0, 0, 0, 0 }; |
| struct drm_mode_map_dumb map_dumb; |
| |
| memset(&create_dumb, 0, sizeof(create_dumb)); |
| create_dumb.width = width; |
| create_dumb.height = height; |
| create_dumb.bpp = 32; |
| |
| rc = drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_dumb); |
| if (rc < 0) { |
| printf("Unable to create dumb buffer: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| buffer->size = create_dumb.size; |
| buffer->pitches[0] = create_dumb.pitch; |
| buffer->offsets[0] = 0; |
| buffer->handles[0] = create_dumb.handle; |
| |
| rc = drmModeAddFB2WithModifiers(fd, width, height, DRM_FORMAT_ARGB8888, |
| buffer->handles, buffer->pitches, |
| buffer->offsets, modifiers, &buffer->framebuffer_id, flags); |
| if (rc < 0) { |
| printf("Unable to add framebuffer for osd plane: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| memset(&map_dumb, 0, sizeof(map_dumb)); |
| map_dumb.handle = buffer->handles[0]; |
| rc = drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map_dumb); |
| if (rc < 0) { |
| printf("Unable to map buffer: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| data = mmap(0, buffer->size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, |
| map_dumb.offset); |
| if (data == MAP_FAILED) { |
| printf("Unable to mmap buffer: %s\n", strerror(errno)); |
| return -1; |
| } |
| |
| /* make it transparent */ |
| memset(data, 0, buffer->size); |
| |
| munmap(data, buffer->size); |
| return 0; |
| } |
| |
| int config_sys_node(const char* path, const char* value) |
| { |
| int fd; |
| /* enable video plane */ |
| fd = open(path, O_RDWR); |
| if (fd < 0) { |
| printf("fail to open %s\n", path); |
| return -1; |
| } |
| if (write(fd, value, strlen(value)) != strlen(value)) { |
| printf("fail to write %s to %s\n", value, path); |
| close(fd); |
| return -1; |
| } |
| close(fd); |
| |
| return 0; |
| } |
| |
| int display_engine_start(int smode) |
| { |
| unsigned int plane_id; |
| int rc; |
| drmModeModeInfo mode; |
| |
| secure_mode = smode; |
| |
| drm_fd = drmOpen("meson", drm_master_dev_name); |
| if (drm_fd < 0) { |
| printf("Unable to open DRM node: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| drm_cli_fd = drmOpen("meson", drm_cli_dev_name); |
| if (drm_cli_fd < 0) { |
| printf("Unable to open client DRM node: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| rc = drmSetClientCap(drm_fd, DRM_CLIENT_CAP_ATOMIC, 1); |
| if (rc < 0) { |
| fprintf(stderr, "Unable to set DRM atomic capability: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| rc = drmSetClientCap(drm_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); |
| if (rc < 0) { |
| fprintf(stderr, |
| "Unable to set DRM universal planes capability: %s\n", |
| strerror(errno)); |
| return -1; |
| } |
| |
| /* prepare all connectors and CRTCs */ |
| rc = init_drm(drm_fd, &mode); |
| if (rc) { |
| fprintf(stderr, "modeset_prepare fail\n"); |
| return -1; |
| } |
| |
| plane_id = global_plane_id; |
| if (rc < 0) { |
| printf("Unable to select DRM plane for CRTC %d\n", |
| setup.crtc_id); |
| return -1; |
| } |
| |
| setup.crtc_width = mode.hdisplay; |
| setup.crtc_height = mode.vdisplay; |
| setup.scaled_width = mode.hdisplay; |
| setup.scaled_height = mode.vdisplay; |
| |
| memcpy(&setup.mode, &mode, sizeof(drmModeModeInfo)); |
| setup.plane_id = plane_id; |
| |
| printf("plane: %u, crtc: %u, encoder:%u, connector: %u.\n", |
| setup.plane_id, setup.crtc_id, |
| setup.encoder_id, setup.connector_id); |
| |
| rc = discover_properties(drm_fd, &setup.properties_ids); |
| if (rc < 0) { |
| fprintf(stderr, "Unable to discover DRM properties\n"); |
| return -1; |
| } |
| |
| setup.plane = calloc(1, sizeof(struct plane)); |
| setup.crtc = calloc(1, sizeof(struct crtc)); |
| setup.connector = calloc(1, sizeof(struct connector)); |
| |
| #define get_resource(type, Type, id) do { \ |
| setup.type->type = drmModeGet##Type(drm_fd, id); \ |
| if (!setup.type->type) { \ |
| printf("could not get %s %i: %s\n", \ |
| #type, id, strerror(errno)); \ |
| return -1; \ |
| } \ |
| } while (0) |
| |
| get_resource(plane, Plane, setup.plane_id); |
| get_resource(crtc, Crtc, setup.crtc_id); |
| get_resource(connector, Connector, setup.connector_id); |
| |
| #define get_properties(type, TYPE, id) do { \ |
| uint32_t i; \ |
| setup.type->props = drmModeObjectGetProperties(drm_fd, \ |
| id, DRM_MODE_OBJECT_##TYPE); \ |
| if (!setup.type->props) { \ |
| printf("could not get %s %u properties: %s\n", \ |
| #type, id, strerror(errno)); \ |
| return -1; \ |
| } \ |
| setup.type->props_info = calloc(setup.type->props->count_props, \ |
| sizeof(setup.type->props_info)); \ |
| for (i = 0; i < setup.type->props->count_props; i++) { \ |
| setup.type->props_info[i] = drmModeGetProperty(drm_fd, \ |
| setup.type->props->props[i]); \ |
| } \ |
| } while (0) |
| |
| get_properties(plane, PLANE, setup.plane_id); |
| get_properties(crtc, CRTC, setup.crtc_id); |
| get_properties(connector, CONNECTOR, setup.connector_id); |
| |
| /* make osd transparent */ |
| rc = OsdBufferCreate(drm_cli_fd, setup.crtc_width, setup.crtc_height, &osd_gem_buf); |
| if (rc) { |
| printf("OsdBufferCreate fail %d\n", rc); |
| return -1; |
| } |
| rc = drmModeSetCrtc(drm_fd, setup.crtc_id, osd_gem_buf.framebuffer_id, 0, 0, |
| &setup.connector_id, 1, &setup.mode); |
| if (rc) { |
| printf("drmModeSetCrtc fail %d\n", rc); |
| return -1; |
| } |
| |
| /* video master mode for testing */ |
| config_sys_node(TSYNC_MODE, "0"); |
| config_sys_node(TSYNC_PCRSCR, "0"); |
| config_sys_node(VPTS_INC_UPINT, "1"); |
| |
| log_set_level((log_level >> 1) & 0x7); |
| aml_dis.avsync = av_sync_create(0, 2, 2, 90000/mode.vrefresh); |
| if (!aml_dis.avsync) { |
| printf("create avsync fails\n"); |
| return -1; |
| } |
| return 0; |
| } |
| |
| static int frame_destroy(struct drm_frame* drm_f) |
| { |
| int rc; |
| struct gem_buffer *gem_buf = drm_f->gem; |
| |
| rc = close_buffer(drm_cli_fd, gem_buf); |
| free(gem_buf); |
| free(drm_f); |
| return rc; |
| } |
| |
| struct drm_frame* display_create_buffer(unsigned int width, unsigned int height, |
| enum frame_format format, int planes_count) |
| { |
| int rc; |
| struct drm_frame* frame = calloc(1, sizeof(*frame)); |
| struct gem_buffer *gem_buf = calloc(1, sizeof(*gem_buf)); |
| |
| if (!frame || !gem_buf) { |
| printf("oom\n"); |
| return NULL; |
| } |
| |
| gem_buf->width = width; |
| gem_buf->height = height; |
| frame->gem = gem_buf; |
| frame->destroy = frame_destroy; |
| |
| gem_buf->planes_count = planes_count; |
| |
| rc = create_meson_gem_buffer(drm_cli_fd, format, gem_buf); |
| if (rc < 0) { |
| printf("create_meson_gem_buffer fail %d\n", rc); |
| goto error; |
| } |
| if (planes_count == 2) |
| printf("export_fd: %d/%d\n", |
| gem_buf->export_fds[0], |
| gem_buf->export_fds[1]); |
| else |
| printf("export_fd: %d\n", |
| gem_buf->export_fds[0]); |
| |
| rc = add_framebuffer(drm_cli_fd, gem_buf, |
| format, DRM_FORMAT_MOD_NONE); |
| if (rc < 0) { |
| printf("Unable to add DRM framebuffer\n"); |
| goto error; |
| } |
| |
| return frame; |
| error: |
| close_buffer(drm_cli_fd, gem_buf); |
| if (frame) free(frame); |
| if (gem_buf) free(gem_buf); |
| return NULL; |
| } |
| |
| int display_get_buffer_fds(struct drm_frame* drm_f, int *fd, int cnt) |
| { |
| int i; |
| struct gem_buffer *gem_buf = drm_f->gem; |
| |
| if (gem_buf->planes_count > cnt || !fd) { |
| return -1; |
| } |
| for (i = 0; i < gem_buf->planes_count; i++) |
| fd[i] = gem_buf->export_fds[i]; |
| return 0; |
| } |
| |
| int display_engine_stop() |
| { |
| aml_dis.started = false; |
| pthread_join(aml_dis.disp_t, NULL); |
| close_buffer(drm_cli_fd, &osd_gem_buf); |
| if (aml_dis.avsync) |
| av_sync_destroy(aml_dis.avsync); |
| |
| #if 0 |
| if (aml_dis.queue) |
| free(aml_dis.queue); |
| #endif |
| if (gem_buf) |
| free(gem_buf); |
| if (setup.plane) { |
| if (setup.plane->props_info) |
| free(setup.plane->props_info); |
| free(setup.plane); |
| } |
| if (setup.crtc) { |
| if (setup.crtc->props_info) |
| free(setup.crtc->props_info); |
| free(setup.crtc); |
| } |
| if (setup.connector) { |
| if (setup.connector->props_info) |
| free(setup.connector->props_info); |
| free(setup.connector); |
| } |
| if (drm_fd >= 0) |
| drmClose(drm_fd); |
| if (drm_cli_fd >= 0) |
| drmClose(drm_cli_fd); |
| |
| return 0; |
| } |
| |
| #if 0 |
| static int queue_frame(struct drm_frame* frame) |
| { |
| if (aml_dis.total_num == aml_dis.q_len) |
| return -1; |
| aml_dis.queue[aml_dis.wi] = *frame; |
| if (aml_dis.wi == aml_dis.q_len - 1) |
| aml_dis.wi = 0; |
| else |
| aml_dis.wi++; |
| aml_dis.total_num++; |
| |
| return 0; |
| } |
| |
| static int dequeue_frame(struct drm_frame* frame) |
| { |
| if (!aml_dis.total_num) |
| return -1; |
| *frame = aml_dis.queue[aml_dis.ri]; |
| if (aml_dis.ri == aml_dis.q_len - 1) |
| aml_dis.ri = 0; |
| else |
| aml_dis.ri++; |
| aml_dis.total_num--; |
| |
| return 0; |
| } |
| #endif |
| |
| static void * display_thread_func(void * arg) |
| { |
| struct vframe *sync_frame; |
| struct drm_frame *f = NULL, *f_p1 = NULL, *f_p2 = NULL; |
| drmVBlank vbl; |
| |
| memset(&vbl, 0, sizeof(drmVBlank)); |
| |
| while (aml_dis.started) { |
| int rc; |
| struct gem_buffer* gem_buf; |
| |
| vbl.request.type = DRM_VBLANK_RELATIVE; |
| vbl.request.sequence = 1; |
| vbl.request.signal = 0; |
| |
| rc = drmWaitVBlank(drm_fd, &vbl); |
| if (rc) { |
| printf("drmWaitVBlank error %d\n", rc); |
| return NULL; |
| } |
| |
| sync_frame = av_sync_pop_frame(aml_dis.avsync); |
| if (!sync_frame) |
| continue; |
| |
| f = sync_frame->private; |
| |
| if (!f) { |
| aml_dis.last_frame = true; |
| break; |
| } |
| |
| log_debug("pop frame: %d", f->pts); |
| if (f != f_p1) { |
| gem_buf = f->gem; |
| rc = page_flip(drm_fd, setup.crtc_id, setup.plane_id, |
| &setup.properties_ids, gem_buf); |
| if (rc) { |
| printf("page_flip error\n"); |
| continue; |
| } |
| /* 2 fence delay on video layer, 1 fence delay on osd */ |
| if (f_p2) { |
| display_cb(f_p2->pri_dec); |
| free(f_p2->pri_sync); |
| } |
| |
| f_p2 = f_p1; |
| f_p1 = f; |
| } |
| } |
| printf("quit %s\n", __func__); |
| return NULL; |
| } |
| |
| static void sync_frame_free(struct vframe * sync_frame) |
| { |
| struct drm_frame* drm_f = sync_frame->private; |
| |
| if (drm_f) { |
| display_cb(drm_f->pri_dec); |
| free(sync_frame); |
| } else |
| aml_dis.last_frame = true; |
| } |
| |
| int display_engine_show(struct drm_frame* frame) |
| { |
| int rc; |
| struct vframe* sync_frame; |
| |
| if (!aml_dis.started) { |
| #if 0 |
| aml_dis.queue = (struct drm_frame*)calloc(MAX_BUF_SIZE, sizeof(*aml_dis.queue)); |
| if (!aml_dis.queue) { |
| printf("%s OOM\n", __func__); |
| return -1; |
| } |
| aml_dis.q_len = MAX_BUF_SIZE; |
| aml_dis.ri = aml_dis.wi = aml_dis.total_num = 0; |
| #endif |
| aml_dis.started = true; |
| aml_dis.last_frame = false; |
| |
| rc = pthread_create(&aml_dis.disp_t, NULL, display_thread_func, NULL); |
| if (rc) { |
| printf("create dispay thread fails\n"); |
| return -1; |
| } |
| } |
| |
| sync_frame = calloc(1, sizeof(*sync_frame)); |
| if (!sync_frame) { |
| printf("%s OOM\n", __func__); |
| return -1; |
| } |
| if (!frame->last_flag) { |
| sync_frame->private = frame; |
| sync_frame->pts = frame->pts; |
| frame->pri_sync = sync_frame; |
| //TODO: fill correct duration |
| } |
| sync_frame->duration = 0; |
| sync_frame->free = sync_frame_free; |
| while (aml_dis.started) { |
| if (av_sync_push_frame(aml_dis.avsync, sync_frame)) { |
| usleep(1000); |
| continue; |
| } else |
| break; |
| } |
| //printf("push frame: %d\n", sync_frame->pts); |
| |
| return 0; |
| } |
| |
| int display_engine_register_cb(displayed_cb_func cb) |
| { |
| display_cb = cb; |
| return 0; |
| } |
| |
| int display_wait_for_display() |
| { |
| while (aml_dis.started && !aml_dis.last_frame) { |
| usleep(10); |
| } |
| if (!aml_dis.started) |
| return -1; |
| |
| return 0; |
| } |