Files
fml13v01-buildroot/package/gstreamer1/gst1-plugins-bad/0002-reset-drm-in-atomic-api.patch
T

1028 lines
32 KiB
Diff

Reset DRM to initial state on each playing.
Signed-off-by: Windsome Zeng <windsome.zeng@starfivetech.com>
diff -purN a/sys/kms/gstkmsallocator.c b/sys/kms/gstkmsallocator.c
--- a/sys/kms/gstkmsallocator.c 2022-09-06 11:07:01.464180909 +0800
+++ b/sys/kms/gstkmsallocator.c 2022-09-06 11:03:57.761729431 +0800
@@ -384,6 +384,9 @@ gst_kms_memory_map (GstMemory * mem, gsi
}
kmsmem->bo->ptr = out;
+ /* clear the framebuffer to 0 */
+ memset(out, 0, kmsmem->bo->size);
+
out:
g_atomic_int_inc (&kmsmem->bo->refs);
return kmsmem->bo->ptr;
diff -purN a/sys/kms/gstkmssink.c b/sys/kms/gstkmssink.c
--- a/sys/kms/gstkmssink.c 2022-09-06 11:07:01.464180909 +0800
+++ b/sys/kms/gstkmssink.c 2022-09-06 11:03:57.761729431 +0800
@@ -74,6 +74,8 @@ static GstFlowReturn gst_kms_sink_show_f
static void gst_kms_sink_video_overlay_init (GstVideoOverlayInterface * iface);
static void gst_kms_sink_drain (GstKMSSink * self);
+int gst_kms_sink_reset_drm(GstKMSSink * self);
+
#define parent_class gst_kms_sink_parent_class
G_DEFINE_TYPE_WITH_CODE (GstKMSSink, gst_kms_sink, GST_TYPE_VIDEO_SINK,
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, GST_PLUGIN_NAME, 0,
@@ -818,6 +820,9 @@ retry_find_plane:
g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_WIDTH]);
g_object_notify_by_pspec (G_OBJECT (self), g_properties[PROP_DISPLAY_HEIGHT]);
+ GST_INFO_OBJECT (self, "Reset DRM to initial state using atomic API.");
+ gst_kms_sink_reset_drm (self);
+
gst_kms_sink_update_connector_properties (self);
gst_kms_sink_update_plane_properties (self);
diff -purN a/sys/kms/meson.build b/sys/kms/meson.build
--- a/sys/kms/meson.build 2022-09-06 11:07:01.464180909 +0800
+++ b/sys/kms/meson.build 2022-09-06 11:03:57.761729431 +0800
@@ -3,6 +3,7 @@ kmssink_sources = [
'gstkmsbufferpool.c',
'gstkmssink.c',
'gstkmsutils.c',
+ 'mymodetest.c',
]
if host_system != 'linux'
diff -purN a/sys/kms/mymodetest.c b/sys/kms/mymodetest.c
--- a/sys/kms/mymodetest.c 1970-01-01 08:00:00.000000000 +0800
+++ b/sys/kms/mymodetest.c 2022-09-06 11:03:57.761729431 +0800
@@ -0,0 +1,973 @@
+/*
+ * SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2022 StarFive Technology Co., Ltd.
+ *
+ * Description: Reset drm to initial state in atomic API.
+ * This code is modified from libdrm/modetest. Instead of command line, all
+ * its info comes from GstKMSSink, such as crtc/plane/connector etc.
+ * Windsome Zeng <windsome.zeng@starfivetech.com>
+ */
+
+/*
+ * DRM based mode setting test program
+ * Copyright 2008 Tungsten Graphics
+ * Jakob Bornecrantz <jakob@tungstengraphics.com>
+ * Copyright 2008 Intel Corporation
+ * Jesse Barnes <jesse.barnes@intel.com>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE.
+ */
+
+/*
+ * This fairly simple test program dumps output in a similar format to the
+ * "xrandr" tool everyone knows & loves. It's necessarily slightly different
+ * since the kernel separates outputs into encoder and connector structures,
+ * each with their own unique ID. The program also allows test testing of the
+ * memory management and mode setting APIs by allowing the user to specify a
+ * connector and mode to use for mode setting. If all works as expected, a
+ * blue background should be painted on the monitor attached to the specified
+ * connector after the selected mode is set.
+ *
+ * TODO: use cairo to write the mode info on the selected output once
+ * the mode has been programmed, along with possible test patterns.
+ */
+
+#include <assert.h>
+#include <ctype.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <inttypes.h>
+#include <unistd.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <poll.h>
+#include <sys/time.h>
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#include <math.h>
+
+#include "xf86drm.h"
+#include "xf86drmMode.h"
+#include "drm_fourcc.h"
+
+#include <gst/video/video.h>
+#include "gstkmssink.h"
+
+int gst_kms_sink_reset_drm(GstKMSSink * self);
+
+struct crtc {
+ drmModeCrtc *crtc;
+ drmModeObjectProperties *props;
+ drmModePropertyRes **props_info;
+ drmModeModeInfo *mode;
+};
+
+struct encoder {
+ drmModeEncoder *encoder;
+};
+
+struct connector {
+ drmModeConnector *connector;
+ drmModeObjectProperties *props;
+ drmModePropertyRes **props_info;
+ char *name;
+};
+
+struct fb {
+ drmModeFB *fb;
+};
+
+struct plane {
+ drmModePlane *plane;
+ drmModeObjectProperties *props;
+ drmModePropertyRes **props_info;
+};
+
+struct resources {
+ struct crtc *crtcs;
+ int count_crtcs;
+ struct encoder *encoders;
+ int count_encoders;
+ struct connector *connectors;
+ int count_connectors;
+ struct fb *fbs;
+ int count_fbs;
+ struct plane *planes;
+ uint32_t count_planes;
+};
+
+struct device {
+ int fd;
+
+ struct resources *resources;
+
+ struct {
+ unsigned int width;
+ unsigned int height;
+
+ unsigned int fb_id;
+ struct bo *bo;
+ struct bo *cursor_bo;
+ } mode;
+
+ int use_atomic;
+ drmModeAtomicReq *req;
+};
+
+static inline int64_t U642I64(uint64_t val)
+{
+ return (int64_t)*((int64_t *)&val);
+}
+
+static float mode_vrefresh(drmModeModeInfo *mode)
+{
+ return mode->clock * 1000.00
+ / (mode->htotal * mode->vtotal);
+}
+
+static void free_resources(struct resources *res)
+{
+ int i;
+
+ if (!res)
+ return;
+
+#define free_resource(_res, type, Type) \
+ do { \
+ if (!(_res)->type##s) \
+ break; \
+ for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
+ if (!(_res)->type##s[i].type) \
+ break; \
+ drmModeFree##Type((_res)->type##s[i].type); \
+ } \
+ free((_res)->type##s); \
+ } while (0)
+
+#define free_properties(_res, type) \
+ do { \
+ for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
+ unsigned int j; \
+ for (j = 0; j < res->type##s[i].props->count_props; ++j)\
+ drmModeFreeProperty(res->type##s[i].props_info[j]);\
+ free(res->type##s[i].props_info); \
+ drmModeFreeObjectProperties(res->type##s[i].props); \
+ } \
+ } while (0)
+
+ free_properties(res, plane);
+ free_resource(res, plane, Plane);
+
+ free_properties(res, connector);
+ free_properties(res, crtc);
+
+ for (i = 0; i < res->count_connectors; i++)
+ free(res->connectors[i].name);
+
+ free_resource(res, fb, FB);
+ free_resource(res, connector, Connector);
+ free_resource(res, encoder, Encoder);
+ free_resource(res, crtc, Crtc);
+
+ free(res);
+}
+
+struct type_name {
+ unsigned int type;
+ const char *name;
+};
+
+#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+
+static const char *util_lookup_type_name(unsigned int type,
+ const struct type_name *table,
+ unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++)
+ if (table[i].type == type)
+ return table[i].name;
+
+ return NULL;
+}
+
+static const struct type_name connector_type_names[] = {
+ { DRM_MODE_CONNECTOR_Unknown, "unknown" },
+ { DRM_MODE_CONNECTOR_VGA, "VGA" },
+ { DRM_MODE_CONNECTOR_DVII, "DVI-I" },
+ { DRM_MODE_CONNECTOR_DVID, "DVI-D" },
+ { DRM_MODE_CONNECTOR_DVIA, "DVI-A" },
+ { DRM_MODE_CONNECTOR_Composite, "composite" },
+ { DRM_MODE_CONNECTOR_SVIDEO, "s-video" },
+ { DRM_MODE_CONNECTOR_LVDS, "LVDS" },
+ { DRM_MODE_CONNECTOR_Component, "component" },
+ { DRM_MODE_CONNECTOR_9PinDIN, "9-pin DIN" },
+ { DRM_MODE_CONNECTOR_DisplayPort, "DP" },
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" },
+ { DRM_MODE_CONNECTOR_TV, "TV" },
+ { DRM_MODE_CONNECTOR_eDP, "eDP" },
+ { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" },
+ { DRM_MODE_CONNECTOR_DSI, "DSI" },
+ { DRM_MODE_CONNECTOR_DPI, "DPI" },
+};
+
+static const char *util_lookup_connector_type_name(unsigned int type)
+{
+ return util_lookup_type_name(type, connector_type_names,
+ ARRAY_SIZE(connector_type_names));
+}
+
+static struct resources *get_resources(struct device *dev)
+{
+ drmModeRes *_res;
+ drmModePlaneRes *plane_res;
+ struct resources *res;
+ int i;
+
+ res = calloc(1, sizeof(*res));
+ if (res == 0)
+ return NULL;
+
+ drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
+
+ _res = drmModeGetResources(dev->fd);
+ if (!_res) {
+ fprintf(stderr, "drmModeGetResources failed: %s\n",
+ strerror(errno));
+ free(res);
+ return NULL;
+ }
+
+ res->count_crtcs = _res->count_crtcs;
+ res->count_encoders = _res->count_encoders;
+ res->count_connectors = _res->count_connectors;
+ res->count_fbs = _res->count_fbs;
+
+ res->crtcs = calloc(res->count_crtcs, sizeof(*res->crtcs));
+ res->encoders = calloc(res->count_encoders, sizeof(*res->encoders));
+ res->connectors = calloc(res->count_connectors, sizeof(*res->connectors));
+ res->fbs = calloc(res->count_fbs, sizeof(*res->fbs));
+
+ if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs) {
+ drmModeFreeResources(_res);
+ goto error;
+ }
+
+#define get_resource(_res, __res, type, Type) \
+ do { \
+ for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
+ uint32_t type##id = (__res)->type##s[i]; \
+ (_res)->type##s[i].type = \
+ drmModeGet##Type(dev->fd, type##id); \
+ if (!(_res)->type##s[i].type) \
+ fprintf(stderr, "could not get %s %i: %s\n", \
+ #type, type##id, \
+ strerror(errno)); \
+ } \
+ } while (0)
+
+ get_resource(res, _res, crtc, Crtc);
+ get_resource(res, _res, encoder, Encoder);
+ get_resource(res, _res, connector, Connector);
+ get_resource(res, _res, fb, FB);
+
+ drmModeFreeResources(_res);
+
+ /* Set the name of all connectors based on the type name and the per-type ID. */
+ for (i = 0; i < res->count_connectors; i++) {
+ struct connector *connector = &res->connectors[i];
+ drmModeConnector *conn = connector->connector;
+ int num;
+
+ num = asprintf(&connector->name, "%s-%u",
+ util_lookup_connector_type_name(conn->connector_type),
+ conn->connector_type_id);
+ if (num < 0)
+ goto error;
+ }
+
+#define get_properties(_res, type, Type) \
+ do { \
+ for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
+ struct type *obj = &res->type##s[i]; \
+ unsigned int j; \
+ obj->props = \
+ drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
+ DRM_MODE_OBJECT_##Type); \
+ if (!obj->props) { \
+ fprintf(stderr, \
+ "could not get %s %i properties: %s\n", \
+ #type, obj->type->type##_id, \
+ strerror(errno)); \
+ continue; \
+ } \
+ obj->props_info = calloc(obj->props->count_props, \
+ sizeof(*obj->props_info)); \
+ if (!obj->props_info) \
+ continue; \
+ for (j = 0; j < obj->props->count_props; ++j) \
+ obj->props_info[j] = \
+ drmModeGetProperty(dev->fd, obj->props->props[j]); \
+ } \
+ } while (0)
+
+ get_properties(res, crtc, CRTC);
+ get_properties(res, connector, CONNECTOR);
+
+ for (i = 0; i < res->count_crtcs; ++i)
+ res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
+
+ plane_res = drmModeGetPlaneResources(dev->fd);
+ if (!plane_res) {
+ fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
+ strerror(errno));
+ return res;
+ }
+
+ res->count_planes = plane_res->count_planes;
+
+ res->planes = calloc(res->count_planes, sizeof(*res->planes));
+ if (!res->planes) {
+ drmModeFreePlaneResources(plane_res);
+ goto error;
+ }
+
+ get_resource(res, plane_res, plane, Plane);
+ drmModeFreePlaneResources(plane_res);
+ get_properties(res, plane, PLANE);
+
+ return res;
+
+error:
+ free_resources(res);
+ return NULL;
+}
+
+static struct crtc *get_crtc_by_id(struct device *dev, uint32_t id)
+{
+ int i;
+
+ for (i = 0; i < dev->resources->count_crtcs; ++i) {
+ drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
+ if (crtc && crtc->crtc_id == id)
+ return &dev->resources->crtcs[i];
+ }
+
+ return NULL;
+}
+
+static uint32_t get_crtc_mask(struct device *dev, struct crtc *crtc)
+{
+ unsigned int i;
+
+ for (i = 0; i < (unsigned int)dev->resources->count_crtcs; i++) {
+ if (crtc->crtc->crtc_id == dev->resources->crtcs[i].crtc->crtc_id)
+ return 1 << i;
+ }
+ /* Unreachable: crtc->crtc is one of resources->crtcs[] */
+ /* Don't return zero or static analysers will complain */
+ abort();
+ return 0;
+}
+
+static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
+{
+ drmModeConnector *connector;
+ int i;
+
+ for (i = 0; i < dev->resources->count_connectors; i++) {
+ connector = dev->resources->connectors[i].connector;
+ if (connector && connector->connector_id == id)
+ return connector;
+ }
+
+ return NULL;
+}
+
+static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
+{
+ drmModeEncoder *encoder;
+ int i;
+
+ for (i = 0; i < dev->resources->count_encoders; i++) {
+ encoder = dev->resources->encoders[i].encoder;
+ if (encoder && encoder->encoder_id == id)
+ return encoder;
+ }
+
+ return NULL;
+}
+
+/* -----------------------------------------------------------------------------
+ * Pipes and planes
+ */
+
+/*
+ * Mode setting with the kernel interfaces is a bit of a chore.
+ * First you have to find the connector in question and make sure the
+ * requested mode is available.
+ * Then you need to find the encoder attached to that connector so you
+ * can bind it with a free crtc.
+ */
+struct pipe_arg {
+ const char **cons;
+ uint32_t *con_ids;
+ unsigned int num_cons;
+ uint32_t crtc_id;
+ char mode_str[64];
+ char format_str[5];
+ float vrefresh;
+ unsigned int fourcc;
+ drmModeModeInfo *mode;
+ struct crtc *crtc;
+ unsigned int fb_id[2], current_fb_id;
+ struct timeval start;
+
+ int swap_count;
+};
+
+struct plane_arg {
+ uint32_t plane_id; /* the id of plane to use */
+ uint32_t crtc_id; /* the id of CRTC to bind to */
+ bool has_position;
+ int32_t x, y;
+ uint32_t w, h;
+ double scale;
+ unsigned int fb_id;
+ unsigned int old_fb_id;
+ struct bo *bo;
+ struct bo *old_bo;
+ char format_str[5]; /* need to leave room for terminating \0 */
+ unsigned int fourcc;
+};
+
+static drmModeModeInfo *
+connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
+ const float vrefresh)
+{
+ drmModeConnector *connector;
+ drmModeModeInfo *mode;
+ int i;
+
+ connector = get_connector_by_id(dev, con_id);
+ if (!connector || !connector->count_modes)
+ return NULL;
+
+ /* Pick by Index */
+ if (mode_str[0] == '#') {
+ int index = atoi(mode_str + 1);
+
+ if (index >= connector->count_modes || index < 0)
+ return NULL;
+ return &connector->modes[index];
+ }
+
+ /* Pick by Name */
+ for (i = 0; i < connector->count_modes; i++) {
+ mode = &connector->modes[i];
+ if (!strcmp(mode->name, mode_str)) {
+ /* If the vertical refresh frequency is not specified
+ * then return the first mode that match with the name.
+ * Else, return the mode that match the name and
+ * the specified vertical refresh frequency.
+ */
+ if (vrefresh == 0)
+ return mode;
+ else if (fabs(mode_vrefresh(mode) - vrefresh) < 0.005)
+ return mode;
+ }
+ }
+
+ return NULL;
+}
+
+static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
+{
+ uint32_t possible_crtcs = ~0;
+ uint32_t active_crtcs = 0;
+ unsigned int crtc_idx;
+ unsigned int i;
+ int j;
+
+ for (i = 0; i < pipe->num_cons; ++i) {
+ uint32_t crtcs_for_connector = 0;
+ drmModeConnector *connector;
+ drmModeEncoder *encoder;
+ struct crtc *crtc;
+
+ connector = get_connector_by_id(dev, pipe->con_ids[i]);
+ if (!connector)
+ return NULL;
+
+ for (j = 0; j < connector->count_encoders; ++j) {
+ encoder = get_encoder_by_id(dev, connector->encoders[j]);
+ if (!encoder)
+ continue;
+
+ crtcs_for_connector |= encoder->possible_crtcs;
+ crtc = get_crtc_by_id(dev, encoder->crtc_id);
+ if (!crtc)
+ continue;
+ active_crtcs |= get_crtc_mask(dev, crtc);
+ }
+
+ possible_crtcs &= crtcs_for_connector;
+ }
+
+ if (!possible_crtcs)
+ return NULL;
+
+ /* Return the first possible and active CRTC if one exists, or the first
+ * possible CRTC otherwise.
+ */
+ if (possible_crtcs & active_crtcs)
+ crtc_idx = ffs(possible_crtcs & active_crtcs);
+ else
+ crtc_idx = ffs(possible_crtcs);
+
+ return &dev->resources->crtcs[crtc_idx - 1];
+}
+
+static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
+{
+ drmModeModeInfo *mode = NULL;
+ int i;
+
+ pipe->mode = NULL;
+
+ for (i = 0; i < (int)pipe->num_cons; i++) {
+ mode = connector_find_mode(dev, pipe->con_ids[i],
+ pipe->mode_str, pipe->vrefresh);
+ if (mode == NULL) {
+ if (pipe->vrefresh)
+ fprintf(stderr,
+ "failed to find mode "
+ "\"%s-%.2fHz\" for connector %d\n",
+ pipe->mode_str, pipe->vrefresh, pipe->con_ids[i]);
+ else
+ fprintf(stderr,
+ "failed to find mode \"%s\" for connector %d\n",
+ pipe->mode_str, pipe->con_ids[i]);
+ return -EINVAL;
+ }
+ }
+
+ /* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
+ * locate a CRTC that can be attached to all the connectors.
+ */
+ if (pipe->crtc_id != (uint32_t)-1) {
+ pipe->crtc = get_crtc_by_id(dev, pipe->crtc_id);
+ } else {
+ pipe->crtc = pipe_find_crtc(dev, pipe);
+ pipe->crtc_id = pipe->crtc->crtc->crtc_id;
+ }
+
+ if (!pipe->crtc) {
+ fprintf(stderr, "failed to find CRTC for pipe\n");
+ return -EINVAL;
+ }
+
+ pipe->mode = mode;
+ pipe->crtc->mode = mode;
+
+ return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Properties
+ */
+
+struct property_arg {
+ uint32_t obj_id;
+ uint32_t obj_type;
+ char name[DRM_PROP_NAME_LEN+1];
+ uint32_t prop_id;
+ uint64_t value;
+ bool optional;
+};
+
+static bool set_property(struct device *dev, struct property_arg *p)
+{
+ drmModeObjectProperties *props = NULL;
+ drmModePropertyRes **props_info = NULL;
+ const char *obj_type;
+ int ret;
+ int i;
+
+ p->obj_type = 0;
+ p->prop_id = 0;
+
+#define find_object(_res, type, Type) \
+ do { \
+ for (i = 0; i < (int)(_res)->count_##type##s; ++i) { \
+ struct type *obj = &(_res)->type##s[i]; \
+ if (obj->type->type##_id != p->obj_id) \
+ continue; \
+ p->obj_type = DRM_MODE_OBJECT_##Type; \
+ obj_type = #Type; \
+ props = obj->props; \
+ props_info = obj->props_info; \
+ } \
+ } while(0) \
+
+ find_object(dev->resources, crtc, CRTC);
+ if (p->obj_type == 0)
+ find_object(dev->resources, connector, CONNECTOR);
+ if (p->obj_type == 0)
+ find_object(dev->resources, plane, PLANE);
+ if (p->obj_type == 0) {
+ fprintf(stderr, "Object %i not found, can't set property\n",
+ p->obj_id);
+ return false;
+ }
+
+ if (!props) {
+ fprintf(stderr, "%s %i has no properties\n",
+ obj_type, p->obj_id);
+ return false;
+ }
+
+ for (i = 0; i < (int)props->count_props; ++i) {
+ if (!props_info[i])
+ continue;
+ if (strcmp(props_info[i]->name, p->name) == 0)
+ break;
+ }
+
+ if (i == (int)props->count_props) {
+ if (!p->optional)
+ fprintf(stderr, "%s %i has no %s property\n",
+ obj_type, p->obj_id, p->name);
+ return false;
+ }
+
+ p->prop_id = props->props[i];
+
+ if (!dev->use_atomic)
+ ret = drmModeObjectSetProperty(dev->fd, p->obj_id, p->obj_type,
+ p->prop_id, p->value);
+ else
+ ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
+
+ if (ret < 0)
+ fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
+ obj_type, p->obj_id, p->name, p->value, strerror(errno));
+
+ return true;
+}
+
+/* -------------------------------------------------------------------------- */
+
+static void add_property(struct device *dev, uint32_t obj_id,
+ const char *name, uint64_t value)
+{
+ struct property_arg p;
+
+ p.obj_id = obj_id;
+ strcpy(p.name, name);
+ p.value = value;
+
+ set_property(dev, &p);
+}
+
+static bool add_property_optional(struct device *dev, uint32_t obj_id,
+ const char *name, uint64_t value)
+{
+ struct property_arg p;
+
+ p.obj_id = obj_id;
+ strcpy(p.name, name);
+ p.value = value;
+ p.optional = true;
+
+ return set_property(dev, &p);
+}
+
+static void util_smpte_c8_gamma(unsigned size, struct drm_color_lut *lut)
+{
+ if (size < 7 + 7 + 8) {
+ printf("Error: gamma too small: %d < %d\n", size, 7 + 7 + 8);
+ return;
+ }
+ memset(lut, 0, size * sizeof(struct drm_color_lut));
+
+#define FILL_COLOR(idx, r, g, b) \
+ lut[idx].red = (r) << 8; \
+ lut[idx].green = (g) << 8; \
+ lut[idx].blue = (b) << 8
+
+ FILL_COLOR( 0, 192, 192, 192); /* grey */
+ FILL_COLOR( 1, 192, 192, 0 ); /* yellow */
+ FILL_COLOR( 2, 0, 192, 192); /* cyan */
+ FILL_COLOR( 3, 0, 192, 0 ); /* green */
+ FILL_COLOR( 4, 192, 0, 192); /* magenta */
+ FILL_COLOR( 5, 192, 0, 0 ); /* red */
+ FILL_COLOR( 6, 0, 0, 192); /* blue */
+
+ FILL_COLOR( 7, 0, 0, 192); /* blue */
+ FILL_COLOR( 8, 19, 19, 19 ); /* black */
+ FILL_COLOR( 9, 192, 0, 192); /* magenta */
+ FILL_COLOR(10, 19, 19, 19 ); /* black */
+ FILL_COLOR(11, 0, 192, 192); /* cyan */
+ FILL_COLOR(12, 19, 19, 19 ); /* black */
+ FILL_COLOR(13, 192, 192, 192); /* grey */
+
+ FILL_COLOR(14, 0, 33, 76); /* in-phase */
+ FILL_COLOR(15, 255, 255, 255); /* super white */
+ FILL_COLOR(16, 50, 0, 106); /* quadrature */
+ FILL_COLOR(17, 19, 19, 19); /* black */
+ FILL_COLOR(18, 9, 9, 9); /* 3.5% */
+ FILL_COLOR(19, 19, 19, 19); /* 7.5% */
+ FILL_COLOR(20, 29, 29, 29); /* 11.5% */
+ FILL_COLOR(21, 19, 19, 19); /* black */
+
+#undef FILL_COLOR
+}
+
+static void set_gamma(struct device *dev, unsigned crtc_id, unsigned fourcc)
+{
+ unsigned blob_id = 0;
+ /* TODO: support 1024-sized LUTs, when the use-case arises */
+ struct drm_color_lut gamma_lut[256];
+ int i, ret;
+
+ if (fourcc == DRM_FORMAT_C8) {
+ /* TODO: Add C8 support for more patterns */
+ util_smpte_c8_gamma(256, gamma_lut);
+ drmModeCreatePropertyBlob(dev->fd, gamma_lut, sizeof(gamma_lut), &blob_id);
+ } else {
+ for (i = 0; i < 256; i++) {
+ gamma_lut[i].red =
+ gamma_lut[i].green =
+ gamma_lut[i].blue = i << 8;
+ }
+ }
+
+ add_property_optional(dev, crtc_id, "DEGAMMA_LUT", 0);
+ add_property_optional(dev, crtc_id, "CTM", 0);
+ if (!add_property_optional(dev, crtc_id, "GAMMA_LUT", blob_id)) {
+ uint16_t r[256], g[256], b[256];
+
+ for (i = 0; i < 256; i++) {
+ r[i] = gamma_lut[i].red;
+ g[i] = gamma_lut[i].green;
+ b[i] = gamma_lut[i].blue;
+ }
+
+ ret = drmModeCrtcSetGamma(dev->fd, crtc_id, 256, r, g, b);
+ if (ret)
+ fprintf(stderr, "failed to set gamma: %s\n", strerror(errno));
+ }
+}
+
+static void atomic_set_planes(struct device *dev, struct plane_arg *p,
+ unsigned int count)
+{
+ if (p && (count > 0))
+ set_gamma(dev, p[0].crtc_id, p[0].fourcc);
+}
+
+static void atomic_clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ add_property(dev, p[i].plane_id, "FB_ID", 0);
+ add_property(dev, p[i].plane_id, "CRTC_ID", 0);
+ add_property(dev, p[i].plane_id, "SRC_X", 0);
+ add_property(dev, p[i].plane_id, "SRC_Y", 0);
+ add_property(dev, p[i].plane_id, "SRC_W", 0);
+ add_property(dev, p[i].plane_id, "SRC_H", 0);
+ add_property(dev, p[i].plane_id, "CRTC_X", 0);
+ add_property(dev, p[i].plane_id, "CRTC_Y", 0);
+ add_property(dev, p[i].plane_id, "CRTC_W", 0);
+ add_property(dev, p[i].plane_id, "CRTC_H", 0);
+ }
+}
+
+static void atomic_clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
+{
+ unsigned int i;
+
+ for (i = 0; i < count; i++) {
+ if (p[i].fb_id) {
+ drmModeRmFB(dev->fd, p[i].fb_id);
+ p[i].fb_id = 0;
+ }
+ if (p[i].old_fb_id) {
+ drmModeRmFB(dev->fd, p[i].old_fb_id);
+ p[i].old_fb_id = 0;
+ }
+ }
+}
+
+static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
+{
+ unsigned int i, j;
+ int ret;
+
+ for (i = 0; i < count; i++) {
+ struct pipe_arg *pipe = &pipes[i];
+ ret = pipe_find_crtc_and_mode(dev, pipe);
+ if (ret < 0)
+ continue;
+ }
+
+ for (i = 0; i < count; i++) {
+ struct pipe_arg *pipe = &pipes[i];
+ uint32_t blob_id;
+
+ if (pipe->mode == NULL)
+ continue;
+
+ for (j = 0; j < pipe->num_cons; ++j) {
+ add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc_id);
+ }
+
+ drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
+ add_property(dev, pipe->crtc_id, "MODE_ID", blob_id);
+ add_property(dev, pipe->crtc_id, "ACTIVE", 1);
+ }
+}
+
+static void atomic_clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
+{
+ unsigned int i;
+ unsigned int j;
+
+ for (i = 0; i < count; i++) {
+ struct pipe_arg *pipe = &pipes[i];
+
+ if (pipe->mode == NULL)
+ continue;
+
+ for (j = 0; j < pipe->num_cons; ++j)
+ add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
+
+ add_property(dev, pipe->crtc_id, "MODE_ID", 0);
+ add_property(dev, pipe->crtc_id, "ACTIVE", 0);
+ }
+}
+
+int
+gst_kms_sink_reset_drm(GstKMSSink * self)
+{
+ int ret = -1;
+ struct device dev;
+ uint64_t cap = 0;
+
+ const unsigned int connector_count = 1, plane_count = 1;
+ struct pipe_arg pipe_args[1];
+ struct plane_arg plane_args[1];
+ uint32_t connectors[1];
+ GValueArray *formats = NULL;
+ GstStructure *st = NULL;
+
+ if (!self || (self->fd < 0))
+ return ret;
+
+ memset (&dev, 0, sizeof(dev));
+ memset (&plane_args, 0, sizeof(*plane_args));
+ memset (&pipe_args, 0, sizeof(*pipe_args));
+
+ dev.fd = self->fd;
+ ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
+ if (ret) {
+ fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
+ goto out;
+ }
+
+ ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
+ if (ret || cap == 0) {
+ fprintf(stderr, "driver doesn't support the dumb buffer API\n");
+ goto out;
+ }
+
+ dev.use_atomic = 1;
+ dev.resources = get_resources(&dev);
+ if (!dev.resources)
+ goto out;
+
+ plane_args[0].plane_id = self->plane_id; /* the id of plane to use */
+ plane_args[0].crtc_id = self->crtc_id; /* the id of CRTC to bind to */
+ plane_args[0].w = self->render_rect.w;
+ plane_args[0].h = self->render_rect.h;
+ plane_args[0].scale = 1.0;
+ strncpy(plane_args[0].format_str, "NV12", 4);
+
+ /* Find the first supported format and also has correct FOURCC value. */
+ st = gst_caps_get_structure(self->allowed_caps, 0);
+ if (st && gst_structure_get_list (st, "format", &formats)) {
+ for (int i = 0; formats && (i < formats->n_values); ++i) {
+ const gchar *format = g_value_get_string(g_value_array_get_nth(formats, i));
+ if (format && (gst_video_format_to_fourcc(gst_video_format_from_string(format)) != 0)) {
+ strncpy(plane_args[0].format_str, format, 4);
+ break;
+ }
+ }
+
+ g_value_array_free(formats);
+ formats = NULL;
+ }
+
+ plane_args[0].fourcc = gst_video_format_to_fourcc(gst_video_format_from_string(plane_args[0].format_str));
+
+ connectors[0] = self->conn_id;
+ pipe_args[0].con_ids = connectors;
+ pipe_args[0].num_cons = connector_count;
+ pipe_args[0].crtc_id = self->crtc_id;
+ strncpy(pipe_args[0].format_str, plane_args[0].format_str, 4);
+ pipe_args[0].fourcc = plane_args[0].fourcc;
+ snprintf(pipe_args[0].mode_str, sizeof(pipe_args[0].mode_str), "%dx%d", plane_args[0].w, plane_args[0].h);
+
+ dev.req = drmModeAtomicAlloc();
+ set_mode(&dev, pipe_args, connector_count);
+ atomic_set_planes(&dev, plane_args, plane_count);
+ ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+ if (ret) {
+ fprintf(stderr, "Atomic Commit failed [1]\n");
+ goto out;
+ }
+
+ // clear
+ atomic_clear_planes(&dev, plane_args, plane_count);
+ atomic_clear_mode(&dev, pipe_args, connector_count);
+ ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
+ if (ret)
+ fprintf(stderr, "Atomic Commit failed\n");
+ atomic_clear_FB(&dev, plane_args, plane_count);
+ drmModeAtomicFree(dev.req);
+ ret = 0;
+
+out:
+ free_resources(dev.resources);
+
+ ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 0);
+ if (ret)
+ fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
+
+ return ret;
+}