Blame gst-libs/gst/gl/gbm/gstgldisplay_gbm.c

Packit 971217
/*
Packit 971217
 * GStreamer
Packit 971217
 * Copyright (C) 2018 Carlos Rafael Giani <dv@pseudoterminal.org>
Packit 971217
 *
Packit 971217
 * This library is free software; you can redistribute it and/or
Packit 971217
 * modify it under the terms of the GNU Library General Public
Packit 971217
 * License as published by the Free Software Foundation; either
Packit 971217
 * version 2 of the License, or (at your option) any later version.
Packit 971217
 *
Packit 971217
 * This library is distributed in the hope that it will be useful,
Packit 971217
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 971217
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 971217
 * Library General Public License for more details.
Packit 971217
 *
Packit 971217
 * You should have received a copy of the GNU Library General Public
Packit 971217
 * License along with this library; if not, write to the
Packit 971217
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
Packit 971217
 * Boston, MA 02110-1301, USA.
Packit 971217
 */
Packit 971217
Packit 971217
#ifdef HAVE_CONFIG_H
Packit 971217
#include "config.h"
Packit 971217
#endif
Packit 971217
Packit 971217
#include "gstgldisplay_gbm.h"
Packit 971217
#include "gstgl_gbm_utils.h"
Packit 971217
Packit 971217
#include <unistd.h>
Packit 971217
#include <fcntl.h>
Packit 971217
#include <errno.h>
Packit 971217
Packit 971217
GST_DEBUG_CATEGORY (gst_gl_gbm_debug);
Packit 971217
Packit 971217
GST_DEBUG_CATEGORY_STATIC (gst_gl_display_debug);
Packit 971217
#define GST_CAT_DEFAULT gst_gl_display_debug
Packit 971217
Packit 971217
Packit 971217
#define INVALID_CRTC ((guint32)0)
Packit 971217
Packit 971217
Packit 971217
G_DEFINE_TYPE (GstGLDisplayGBM, gst_gl_display_gbm, GST_TYPE_GL_DISPLAY);
Packit 971217
Packit 971217
Packit 971217
static void gst_gl_display_gbm_finalize (GObject * object);
Packit 971217
static guintptr gst_gl_display_gbm_get_handle (GstGLDisplay * display);
Packit 971217
Packit 971217
static guint32 gst_gl_gbm_find_crtc_id_for_encoder (GstGLDisplayGBM *
Packit 971217
    display_gbm, drmModeEncoder const *encoder);
Packit 971217
static guint32 gst_gl_gbm_find_crtc_id_for_connector (GstGLDisplayGBM *
Packit 971217
    display_gbm);
Packit 971217
Packit 971217
static gboolean gst_gl_display_gbm_setup_drm (GstGLDisplayGBM * display_gbm);
Packit 971217
static void gst_gl_display_gbm_shutdown_drm (GstGLDisplayGBM * display_gbm);
Packit 971217
Packit 971217
static gboolean gst_gl_display_gbm_setup_gbm (GstGLDisplayGBM * display_gbm);
Packit 971217
static void gst_gl_display_gbm_shutdown_gbm (GstGLDisplayGBM * display_gbm);
Packit 971217
Packit 971217
Packit 971217
static void
Packit 971217
gst_gl_display_gbm_class_init (GstGLDisplayGBMClass * klass)
Packit 971217
{
Packit 971217
  GST_GL_DISPLAY_CLASS (klass)->get_handle =
Packit 971217
      GST_DEBUG_FUNCPTR (gst_gl_display_gbm_get_handle);
Packit 971217
Packit 971217
  G_OBJECT_CLASS (klass)->finalize = gst_gl_display_gbm_finalize;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
gst_gl_display_gbm_init (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  GstGLDisplay *display = (GstGLDisplay *) display_gbm;
Packit 971217
  display->type = GST_GL_DISPLAY_TYPE_GBM;
Packit 971217
Packit 971217
  display_gbm->drm_fd = -1;
Packit 971217
}
Packit 971217
Packit 971217
static void
Packit 971217
gst_gl_display_gbm_finalize (GObject * object)
Packit 971217
{
Packit 971217
  GstGLDisplayGBM *display_gbm = GST_GL_DISPLAY_GBM (object);
Packit 971217
Packit 971217
  gst_gl_display_gbm_shutdown_gbm (display_gbm);
Packit 971217
  gst_gl_display_gbm_shutdown_drm (display_gbm);
Packit 971217
Packit 971217
  G_OBJECT_CLASS (gst_gl_display_gbm_parent_class)->finalize (object);
Packit 971217
}
Packit 971217
Packit 971217
static guintptr
Packit 971217
gst_gl_display_gbm_get_handle (GstGLDisplay * display)
Packit 971217
{
Packit 971217
  return (guintptr) GST_GL_DISPLAY_GBM (display)->gbm_dev;
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static guint32
Packit 971217
gst_gl_gbm_find_crtc_id_for_encoder (GstGLDisplayGBM * display_gbm,
Packit 971217
    drmModeEncoder const *encoder)
Packit 971217
{
Packit 971217
  int i;
Packit 971217
  for (i = 0; i < display_gbm->drm_mode_resources->count_crtcs; ++i) {
Packit 971217
    /* possible_crtcs is a bitmask as described here:
Packit 971217
     * https://dvdhrm.wordpress.com/2012/09/13/linux-drm-mode-setting-api */
Packit 971217
    guint32 const crtc_mask = 1 << i;
Packit 971217
    guint32 const crtc_id = display_gbm->drm_mode_resources->crtcs[i];
Packit 971217
Packit 971217
    if (encoder->possible_crtcs & crtc_mask)
Packit 971217
      return crtc_id;
Packit 971217
  }
Packit 971217
Packit 971217
  /* No match found */
Packit 971217
  return INVALID_CRTC;
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static guint32
Packit 971217
gst_gl_gbm_find_crtc_id_for_connector (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  int i;
Packit 971217
  for (i = 0; i < display_gbm->drm_mode_connector->count_encoders; ++i) {
Packit 971217
    guint32 encoder_id = display_gbm->drm_mode_connector->encoders[i];
Packit 971217
    drmModeEncoder *encoder =
Packit 971217
        drmModeGetEncoder (display_gbm->drm_fd, encoder_id);
Packit 971217
Packit 971217
    if (encoder != NULL) {
Packit 971217
      guint32 crtc_id =
Packit 971217
          gst_gl_gbm_find_crtc_id_for_encoder (display_gbm, encoder);
Packit 971217
      drmModeFreeEncoder (encoder);
Packit 971217
Packit 971217
      if (crtc_id != INVALID_CRTC)
Packit 971217
        return crtc_id;
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  /* No match found */
Packit 971217
  return INVALID_CRTC;
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static gboolean
Packit 971217
gst_gl_display_gbm_setup_drm (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  int i;
Packit 971217
Packit 971217
  g_assert (display_gbm != NULL);
Packit 971217
  g_assert (display_gbm->drm_fd >= 0);
Packit 971217
Packit 971217
  /* Get the DRM mode resources */
Packit 971217
  display_gbm->drm_mode_resources = drmModeGetResources (display_gbm->drm_fd);
Packit 971217
  if (display_gbm->drm_mode_resources == NULL) {
Packit 971217
    GST_ERROR ("Could not get DRM resources: %s (%d)", g_strerror (errno),
Packit 971217
        errno);
Packit 971217
    goto cleanup;
Packit 971217
  }
Packit 971217
  GST_DEBUG ("Got DRM resources");
Packit 971217
Packit 971217
  /* Find a connected connector. The connector is where the pixel data is
Packit 971217
   * finally sent to, and typically connects to some form of display, like an
Packit 971217
   * HDMI TV, an LVDS panel etc. */
Packit 971217
  {
Packit 971217
    drmModeConnector *connector = NULL;
Packit 971217
Packit 971217
    GST_DEBUG ("Checking %d DRM connector(s)",
Packit 971217
        display_gbm->drm_mode_resources->count_connectors);
Packit 971217
    for (i = 0; i < display_gbm->drm_mode_resources->count_connectors; ++i) {
Packit 971217
      connector = drmModeGetConnector (display_gbm->drm_fd,
Packit 971217
          display_gbm->drm_mode_resources->connectors[i]);
Packit 971217
      GST_DEBUG ("Found DRM connector #%d \"%s\" with ID %" G_GUINT32_FORMAT, i,
Packit 971217
          gst_gl_gbm_get_name_for_drm_connector (connector),
Packit 971217
          connector->connector_id);
Packit 971217
Packit 971217
      if (connector->connection == DRM_MODE_CONNECTED) {
Packit 971217
        GST_DEBUG ("DRM connector #%d is connected", i);
Packit 971217
        break;
Packit 971217
      }
Packit 971217
Packit 971217
      drmModeFreeConnector (connector);
Packit 971217
      connector = NULL;
Packit 971217
    }
Packit 971217
Packit 971217
    if (connector == NULL) {
Packit 971217
      GST_ERROR ("No connected DRM connector found");
Packit 971217
      goto cleanup;
Packit 971217
    }
Packit 971217
Packit 971217
    display_gbm->drm_mode_connector = connector;
Packit 971217
  }
Packit 971217
Packit 971217
  /* Check out what modes are supported by the chosen connector,
Packit 971217
   * and pick either the "preferred" mode or the one with the largest
Packit 971217
   * pixel area. */
Packit 971217
  {
Packit 971217
    int selected_mode_index = -1;
Packit 971217
    int selected_mode_area = -1;
Packit 971217
Packit 971217
    GST_DEBUG ("Checking %d DRM mode(s) from selected connector",
Packit 971217
        display_gbm->drm_mode_connector->count_modes);
Packit 971217
    for (i = 0; i < display_gbm->drm_mode_connector->count_modes; ++i) {
Packit 971217
      drmModeModeInfo *current_mode =
Packit 971217
          &(display_gbm->drm_mode_connector->modes[i]);
Packit 971217
      int current_mode_area = current_mode->hdisplay * current_mode->vdisplay;
Packit 971217
Packit 971217
      GST_DEBUG ("Found DRM mode #%d width/height %" G_GUINT16_FORMAT "/%"
Packit 971217
          G_GUINT16_FORMAT " hsync/vsync start %" G_GUINT16_FORMAT "/%"
Packit 971217
          G_GUINT16_FORMAT " hsync/vsync end %" G_GUINT16_FORMAT "/%"
Packit 971217
          G_GUINT16_FORMAT " htotal/vtotal %" G_GUINT16_FORMAT "/%"
Packit 971217
          G_GUINT16_FORMAT " hskew %" G_GUINT16_FORMAT " vscan %"
Packit 971217
          G_GUINT16_FORMAT " vrefresh %" G_GUINT32_FORMAT " preferred %d", i,
Packit 971217
          current_mode->hdisplay, current_mode->vdisplay,
Packit 971217
          current_mode->hsync_start, current_mode->vsync_start,
Packit 971217
          current_mode->hsync_end, current_mode->vsync_end,
Packit 971217
          current_mode->htotal, current_mode->vtotal, current_mode->hskew,
Packit 971217
          current_mode->vscan, current_mode->vrefresh,
Packit 971217
          (current_mode->type & DRM_MODE_TYPE_PREFERRED) ? TRUE : FALSE);
Packit 971217
Packit 971217
      if ((current_mode->type & DRM_MODE_TYPE_PREFERRED) ||
Packit 971217
          (current_mode_area > selected_mode_area)) {
Packit 971217
        display_gbm->drm_mode_info = current_mode;
Packit 971217
        selected_mode_area = current_mode_area;
Packit 971217
        selected_mode_index = i;
Packit 971217
Packit 971217
        if (current_mode->type & DRM_MODE_TYPE_PREFERRED)
Packit 971217
          break;
Packit 971217
      }
Packit 971217
    }
Packit 971217
Packit 971217
    if (display_gbm->drm_mode_info == NULL) {
Packit 971217
      GST_ERROR ("No usable DRM mode found");
Packit 971217
      goto cleanup;
Packit 971217
    }
Packit 971217
Packit 971217
    GST_DEBUG ("Selected DRM mode #%d", selected_mode_index);
Packit 971217
  }
Packit 971217
Packit 971217
  /* Find an encoder that is attached to the chosen connector. Also find the
Packit 971217
   * index/id of the CRTC associated with this encoder. The encoder takes pixel
Packit 971217
   * data from the CRTC and transmits it to the connector. The CRTC roughly
Packit 971217
   * represents the scanout framebuffer.
Packit 971217
   *
Packit 971217
   * Ultimately, we only care about the CRTC index & ID, so the encoder
Packit 971217
   * reference is discarded here once these are found. The CRTC index is the
Packit 971217
   * index in the m_drm_mode_resources' CRTC array, while the ID is an identifier
Packit 971217
   * used by the DRM to refer to the CRTC universally. (We need the CRTC
Packit 971217
   * information for page flipping and DRM scanout framebuffer configuration.) */
Packit 971217
  {
Packit 971217
    drmModeEncoder *encoder = NULL;
Packit 971217
Packit 971217
    GST_DEBUG ("Checking %d DRM encoder(s)",
Packit 971217
        display_gbm->drm_mode_resources->count_encoders);
Packit 971217
    for (i = 0; i < display_gbm->drm_mode_resources->count_encoders; ++i) {
Packit 971217
      encoder = drmModeGetEncoder (display_gbm->drm_fd,
Packit 971217
          display_gbm->drm_mode_resources->encoders[i]);
Packit 971217
Packit 971217
      GST_DEBUG ("Found DRM encoder #%d \"%s\"", i,
Packit 971217
          gst_gl_gbm_get_name_for_drm_encoder (encoder));
Packit 971217
Packit 971217
      if (encoder->encoder_id == display_gbm->drm_mode_connector->encoder_id) {
Packit 971217
        GST_DEBUG ("DRM encoder #%d corresponds to selected DRM connector "
Packit 971217
            "-> selected", i);
Packit 971217
        break;
Packit 971217
      }
Packit 971217
      drmModeFreeEncoder (encoder);
Packit 971217
      encoder = NULL;
Packit 971217
    }
Packit 971217
Packit 971217
    if (encoder == NULL) {
Packit 971217
      GST_DEBUG ("No encoder found; searching for CRTC ID in the connector");
Packit 971217
      display_gbm->crtc_id =
Packit 971217
          gst_gl_gbm_find_crtc_id_for_connector (display_gbm);
Packit 971217
    } else {
Packit 971217
      GST_DEBUG ("Using CRTC ID from selected encoder");
Packit 971217
      display_gbm->crtc_id = encoder->crtc_id;
Packit 971217
      drmModeFreeEncoder (encoder);
Packit 971217
    }
Packit 971217
Packit 971217
    if (display_gbm->crtc_id == INVALID_CRTC) {
Packit 971217
      GST_ERROR ("No CRTC found");
Packit 971217
      goto cleanup;
Packit 971217
    }
Packit 971217
Packit 971217
    GST_DEBUG ("CRTC with ID %" G_GUINT32_FORMAT " found; now locating it in "
Packit 971217
        "the DRM mode resources CRTC array", display_gbm->crtc_id);
Packit 971217
Packit 971217
    for (i = 0; i < display_gbm->drm_mode_resources->count_crtcs; ++i) {
Packit 971217
      if (display_gbm->drm_mode_resources->crtcs[i] == display_gbm->crtc_id) {
Packit 971217
        display_gbm->crtc_index = i;
Packit 971217
        break;
Packit 971217
      }
Packit 971217
    }
Packit 971217
Packit 971217
    if (display_gbm->crtc_index < 0) {
Packit 971217
      GST_ERROR ("No matching CRTC entry in DRM resources found");
Packit 971217
      goto cleanup;
Packit 971217
    }
Packit 971217
Packit 971217
    GST_DEBUG ("CRTC with ID %" G_GUINT32_FORMAT " can be found at index #%d "
Packit 971217
        "in the DRM mode resources CRTC array", display_gbm->crtc_id,
Packit 971217
        display_gbm->crtc_index);
Packit 971217
  }
Packit 971217
Packit 971217
  GST_DEBUG ("DRM structures initialized");
Packit 971217
  return TRUE;
Packit 971217
Packit 971217
cleanup:
Packit 971217
  gst_gl_display_gbm_shutdown_drm (display_gbm);
Packit 971217
  return FALSE;
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static void
Packit 971217
gst_gl_display_gbm_shutdown_drm (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  g_assert (display_gbm != NULL);
Packit 971217
Packit 971217
  display_gbm->drm_mode_info = NULL;
Packit 971217
Packit 971217
  display_gbm->crtc_index = -1;
Packit 971217
  display_gbm->crtc_id = INVALID_CRTC;
Packit 971217
Packit 971217
  if (display_gbm->drm_mode_connector != NULL) {
Packit 971217
    drmModeFreeConnector (display_gbm->drm_mode_connector);
Packit 971217
    display_gbm->drm_mode_connector = NULL;
Packit 971217
  }
Packit 971217
Packit 971217
  if (display_gbm->drm_mode_resources != NULL) {
Packit 971217
    drmModeFreeResources (display_gbm->drm_mode_resources);
Packit 971217
    display_gbm->drm_mode_resources = NULL;
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static gboolean
Packit 971217
gst_gl_display_gbm_setup_gbm (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  display_gbm->gbm_dev = gbm_create_device (display_gbm->drm_fd);
Packit 971217
  if (display_gbm->gbm_dev == NULL) {
Packit 971217
    GST_ERROR ("Creating GBM device failed");
Packit 971217
    return FALSE;
Packit 971217
  }
Packit 971217
Packit 971217
  GST_DEBUG ("GBM structures initialized");
Packit 971217
  return TRUE;
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static void
Packit 971217
gst_gl_display_gbm_shutdown_gbm (GstGLDisplayGBM * display_gbm)
Packit 971217
{
Packit 971217
  if (display_gbm->gbm_dev != NULL) {
Packit 971217
    gbm_device_destroy (display_gbm->gbm_dev);
Packit 971217
    display_gbm->gbm_dev = NULL;
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
static void
Packit 971217
_init_debug (void)
Packit 971217
{
Packit 971217
  static volatile gsize _init = 0;
Packit 971217
Packit 971217
  if (g_once_init_enter (&_init)) {
Packit 971217
    GST_DEBUG_CATEGORY_GET (gst_gl_display_debug, "gldisplay");
Packit 971217
    GST_DEBUG_CATEGORY_INIT (gst_gl_gbm_debug, "gleglgbm", 0,
Packit 971217
        "Mesa3D EGL GBM debugging");
Packit 971217
    g_once_init_leave (&_init, 1);
Packit 971217
  }
Packit 971217
}
Packit 971217
Packit 971217
Packit 971217
GstGLDisplayGBM *
Packit 971217
gst_gl_display_gbm_new (void)
Packit 971217
{
Packit 971217
  int drm_fd = -1;
Packit 971217
  GstGLDisplayGBM *display;
Packit 971217
  const gchar *drm_node_name;
Packit 971217
Packit 971217
  _init_debug ();
Packit 971217
Packit 971217
  drm_node_name = g_getenv ("GST_GL_GBM_DRM_DEVICE");
Packit 971217
Packit 971217
  if (drm_node_name != NULL) {
Packit 971217
    GST_DEBUG ("attempting to open device %s (specified by the "
Packit 971217
        "GST_GL_GBM_DRM_DEVICE environment variable)", drm_node_name);
Packit 971217
    drm_fd = open (drm_node_name, O_RDWR | O_CLOEXEC);
Packit 971217
    if (drm_fd < 0) {
Packit 971217
      GST_ERROR ("could not open DRM device %s: %s (%d)", drm_node_name,
Packit 971217
          g_strerror (errno), errno);
Packit 971217
      return NULL;
Packit 971217
    }
Packit 971217
  } else {
Packit 971217
    GST_DEBUG ("GST_GL_GBM_DRM_DEVICE environment variable is not "
Packit 971217
        "set - trying to autodetect device");
Packit 971217
    drm_fd = gst_gl_gbm_find_and_open_drm_node ();
Packit 971217
    if (drm_fd < 0) {
Packit 971217
      GST_ERROR ("could not find or open DRM device");
Packit 971217
      return NULL;
Packit 971217
    }
Packit 971217
  }
Packit 971217
Packit 971217
  display = g_object_new (GST_TYPE_GL_DISPLAY_GBM, NULL);
Packit 971217
  display->drm_fd = drm_fd;
Packit 971217
Packit 971217
  if (!gst_gl_display_gbm_setup_drm (display)) {
Packit 971217
    GST_ERROR ("Failed to initialize DRM");
Packit 971217
    goto cleanup;
Packit 971217
  }
Packit 971217
Packit 971217
  if (!gst_gl_display_gbm_setup_gbm (display)) {
Packit 971217
    GST_ERROR ("Failed to initialize GBM");
Packit 971217
    goto cleanup;
Packit 971217
  }
Packit 971217
Packit 971217
  GST_DEBUG ("Created GBM EGL display %p", (gpointer) display);
Packit 971217
Packit 971217
  return display;
Packit 971217
Packit 971217
cleanup:
Packit 971217
  gst_gl_display_gbm_shutdown_gbm (display);
Packit 971217
  gst_gl_display_gbm_shutdown_drm (display);
Packit 971217
  gst_object_unref (G_OBJECT (display));
Packit 971217
  if (drm_fd >= 0)
Packit 971217
    close (drm_fd);
Packit 971217
  return NULL;
Packit 971217
}