Reset DRM to initial state on each playing. Signed-off-by: Windsome Zeng 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 + */ + +/* + * DRM based mode setting test program + * Copyright 2008 Tungsten Graphics + * Jakob Bornecrantz + * Copyright 2008 Intel Corporation + * Jesse Barnes + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if HAVE_SYS_SELECT_H +#include +#endif +#include + +#include "xf86drm.h" +#include "xf86drmMode.h" +#include "drm_fourcc.h" + +#include +#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; +}