Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  drm_client_modeset.c   Sprache: C

 
// SPDX-License-Identifier: MIT
/*
 * Copyright 2018 Noralf Trønnes
 * Copyright (c) 2006-2009 Red Hat Inc.
 * Copyright (c) 2006-2008 Intel Corporation
 *   Jesse Barnes <jesse.barnes@intel.com>
 * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
 */


#include "drm/drm_modeset_lock.h"

#include <linux/export.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/string_helpers.h>

#include <drm/drm_atomic.h>
#include <drm/drm_client.h>
#include <drm/drm_connector.h>
#include <drm/drm_crtc.h>
#include <drm/drm_device.h>
#include <drm/drm_drv.h>
#include <drm/drm_edid.h>
#include <drm/drm_encoder.h>
#include <drm/drm_print.h>

#include "drm_crtc_internal.h"
#include "drm_internal.h"

#define DRM_CLIENT_MAX_CLONED_CONNECTORS 8

struct drm_client_offset {
 int x, y;
};

int drm_client_modeset_create(struct drm_client_dev *client)
{
 struct drm_device *dev = client->dev;
 unsigned int num_crtc = dev->mode_config.num_crtc;
 unsigned int max_connector_count = 1;
 struct drm_mode_set *modeset;
 struct drm_crtc *crtc;
 int i = 0;

 /* Add terminating zero entry to enable index less iteration */
 client->modesets = kcalloc(num_crtc + 1, sizeof(*client->modesets), GFP_KERNEL);
 if (!client->modesets)
  return -ENOMEM;

 mutex_init(&client->modeset_mutex);

 drm_for_each_crtc(crtc, dev)
  client->modesets[i++].crtc = crtc;

 /* Cloning is only supported in the single crtc case. */
 if (num_crtc == 1)
  max_connector_count = DRM_CLIENT_MAX_CLONED_CONNECTORS;

 for (modeset = client->modesets; modeset->crtc; modeset++) {
  modeset->connectors = kcalloc(max_connector_count,
           sizeof(*modeset->connectors), GFP_KERNEL);
  if (!modeset->connectors)
   goto err_free;
 }

 return 0;

err_free:
 drm_client_modeset_free(client);

 return -ENOMEM;
}

static void drm_client_modeset_release(struct drm_client_dev *client)
{
 struct drm_mode_set *modeset;

 drm_client_for_each_modeset(modeset, client) {
  int i;

  drm_mode_destroy(client->dev, modeset->mode);
  modeset->mode = NULL;
  modeset->fb = NULL;

  for (i = 0; i < modeset->num_connectors; i++) {
   drm_connector_put(modeset->connectors[i]);
   modeset->connectors[i] = NULL;
  }
  modeset->num_connectors = 0;
 }
}

void drm_client_modeset_free(struct drm_client_dev *client)
{
 struct drm_mode_set *modeset;

 mutex_lock(&client->modeset_mutex);

 drm_client_modeset_release(client);

 drm_client_for_each_modeset(modeset, client)
  kfree(modeset->connectors);

 mutex_unlock(&client->modeset_mutex);

 mutex_destroy(&client->modeset_mutex);
 kfree(client->modesets);
}

static struct drm_mode_set *
drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc)
{
 struct drm_mode_set *modeset;

 drm_client_for_each_modeset(modeset, client)
  if (modeset->crtc == crtc)
   return modeset;

 return NULL;
}

static const struct drm_display_mode *
drm_connector_get_tiled_mode(struct drm_connector *connector)
{
 const struct drm_display_mode *mode;

 list_for_each_entry(mode, &connector->modes, head) {
  if (mode->hdisplay == connector->tile_h_size &&
      mode->vdisplay == connector->tile_v_size)
   return mode;
 }
 return NULL;
}

static const struct drm_display_mode *
drm_connector_fallback_non_tiled_mode(struct drm_connector *connector)
{
 const struct drm_display_mode *mode;

 list_for_each_entry(mode, &connector->modes, head) {
  if (mode->hdisplay == connector->tile_h_size &&
      mode->vdisplay == connector->tile_v_size)
   continue;
  return mode;
 }
 return NULL;
}

static const struct drm_display_mode *
drm_connector_preferred_mode(struct drm_connector *connector, int width, int height)
{
 const struct drm_display_mode *mode;

 list_for_each_entry(mode, &connector->modes, head) {
  if (mode->hdisplay > width ||
      mode->vdisplay > height)
   continue;
  if (mode->type & DRM_MODE_TYPE_PREFERRED)
   return mode;
 }
 return NULL;
}

static const struct drm_display_mode *
drm_connector_first_mode(struct drm_connector *connector)
{
 return list_first_entry_or_null(&connector->modes,
     struct drm_display_mode, head);
}

static const struct drm_display_mode *
drm_connector_pick_cmdline_mode(struct drm_connector *connector)
{
 const struct drm_cmdline_mode *cmdline_mode;
 const struct drm_display_mode *mode;
 bool prefer_non_interlace;

 /*
 * Find a user-defined mode. If the user gave us a valid
 * mode on the kernel command line, it will show up in this
 * list.
 */


 list_for_each_entry(mode, &connector->modes, head) {
  if (mode->type & DRM_MODE_TYPE_USERDEF)
   return mode;
 }

 cmdline_mode = &connector->cmdline_mode;
 if (cmdline_mode->specified == false)
  return NULL;

 /*
 * Attempt to find a matching mode in the list of modes we
 * have gotten so far.
 */


 prefer_non_interlace = !cmdline_mode->interlace;
again:
 list_for_each_entry(mode, &connector->modes, head) {
  /* check width/height */
  if (mode->hdisplay != cmdline_mode->xres ||
      mode->vdisplay != cmdline_mode->yres)
   continue;

  if (cmdline_mode->refresh_specified) {
   if (drm_mode_vrefresh(mode) != cmdline_mode->refresh)
    continue;
  }

  if (cmdline_mode->interlace) {
   if (!(mode->flags & DRM_MODE_FLAG_INTERLACE))
    continue;
  } else if (prefer_non_interlace) {
   if (mode->flags & DRM_MODE_FLAG_INTERLACE)
    continue;
  }
  return mode;
 }

 if (prefer_non_interlace) {
  prefer_non_interlace = false;
  goto again;
 }

 return NULL;
}

static bool drm_connector_enabled(struct drm_connector *connector, bool strict)
{
 bool enable;

 if (connector->display_info.non_desktop)
  return false;

 if (strict)
  enable = connector->status == connector_status_connected;
 else
  enable = connector->status != connector_status_disconnected;

 return enable;
}

static void drm_client_connectors_enabled(struct drm_connector *connectors[],
       unsigned int connector_count,
       bool enabled[])
{
 bool any_enabled = false;
 struct drm_connector *connector;
 int i = 0;

 for (i = 0; i < connector_count; i++) {
  connector = connectors[i];
  enabled[i] = drm_connector_enabled(connector, true);
  drm_dbg_kms(connector->dev, "[CONNECTOR:%d:%s] enabled? %s\n",
       connector->base.id, connector->name,
       connector->display_info.non_desktop ?
       "non desktop" : str_yes_no(enabled[i]));

  any_enabled |= enabled[i];
 }

 if (any_enabled)
  return;

 for (i = 0; i < connector_count; i++)
  enabled[i] = drm_connector_enabled(connectors[i], false);
}

static void mode_replace(struct drm_device *dev,
    const struct drm_display_mode **dst,
    const struct drm_display_mode *src)
{
 drm_mode_destroy(dev, (struct drm_display_mode *)*dst);

 *dst = src ? drm_mode_duplicate(dev, src) : NULL;
}

static void modes_destroy(struct drm_device *dev,
     const struct drm_display_mode *modes[],
     int count)
{
 int i;

 for (i = 0; i < count; i++)
  mode_replace(dev, &modes[i], NULL);
}

static bool drm_client_target_cloned(struct drm_device *dev,
         struct drm_connector *connectors[],
         unsigned int connector_count,
         const struct drm_display_mode *modes[],
         struct drm_client_offset offsets[],
         bool enabled[], int width, int height)
{
 int count, i;
 bool can_clone = false;
 struct drm_display_mode *dmt_mode;

 /* only contemplate cloning in the single crtc case */
 if (dev->mode_config.num_crtc > 1)
  return false;

 count = 0;
 for (i = 0; i < connector_count; i++) {
  if (enabled[i])
   count++;
 }

 /* only contemplate cloning if more than one connector is enabled */
 if (count <= 1)
  return false;

 /* check the command line or if nothing common pick 1024x768 */
 can_clone = true;
 for (i = 0; i < connector_count; i++) {
  int j;

  if (!enabled[i])
   continue;

  mode_replace(dev, &modes[i],
        drm_connector_pick_cmdline_mode(connectors[i]));
  if (!modes[i]) {
   can_clone = false;
   break;
  }
  for (j = 0; j < i; j++) {
   if (!enabled[j])
    continue;
   if (!drm_mode_match(modes[j], modes[i],
         DRM_MODE_MATCH_TIMINGS |
         DRM_MODE_MATCH_CLOCK |
         DRM_MODE_MATCH_FLAGS |
         DRM_MODE_MATCH_3D_FLAGS))
    can_clone = false;
  }
 }

 if (can_clone) {
  drm_dbg_kms(dev, "can clone using command line\n");
  return true;
 }

 /* try and find a 1024x768 mode on each connector */
 can_clone = true;
 dmt_mode = drm_mode_find_dmt(dev, 1024, 768, 60, false);

 if (!dmt_mode)
  goto fail;

 for (i = 0; i < connector_count; i++) {
  const struct drm_display_mode *mode;

  if (!enabled[i])
   continue;

  list_for_each_entry(mode, &connectors[i]->modes, head) {
   if (drm_mode_match(mode, dmt_mode,
        DRM_MODE_MATCH_TIMINGS |
        DRM_MODE_MATCH_CLOCK |
        DRM_MODE_MATCH_FLAGS |
        DRM_MODE_MATCH_3D_FLAGS))
    mode_replace(dev, &modes[i], mode);
  }
  if (!modes[i])
   can_clone = false;
 }
 drm_mode_destroy(dev, dmt_mode);

 if (can_clone) {
  drm_dbg_kms(dev, "can clone using 1024x768\n");
  return true;
 }
fail:
 drm_info(dev, "kms: can't enable cloning when we probably wanted to.\n");
 return false;
}

static int drm_client_get_tile_offsets(struct drm_device *dev,
           struct drm_connector *connectors[],
           unsigned int connector_count,
           const struct drm_display_mode *modes[],
           struct drm_client_offset offsets[],
           int idx,
           int h_idx, int v_idx)
{
 int i;
 int hoffset = 0, voffset = 0;

 for (i = 0; i < connector_count; i++) {
  struct drm_connector *connector = connectors[i];

  if (!connector->has_tile)
   continue;

  if (!modes[i] && (h_idx || v_idx)) {
   drm_dbg_kms(dev,
        "[CONNECTOR:%d:%s] no modes for connector tiled %d\n",
        connector->base.id, connector->name, i);
   continue;
  }
  if (connector->tile_h_loc < h_idx)
   hoffset += modes[i]->hdisplay;

  if (connector->tile_v_loc < v_idx)
   voffset += modes[i]->vdisplay;
 }
 offsets[idx].x = hoffset;
 offsets[idx].y = voffset;
 drm_dbg_kms(dev, "returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx);
 return 0;
}

static bool drm_client_target_preferred(struct drm_device *dev,
     struct drm_connector *connectors[],
     unsigned int connector_count,
     const struct drm_display_mode *modes[],
     struct drm_client_offset offsets[],
     bool enabled[], int width, int height)
{
 const u64 mask = BIT_ULL(connector_count) - 1;
 u64 conn_configured = 0;
 int tile_pass = 0;
 int num_tiled_conns = 0;
 int i;

 for (i = 0; i < connector_count; i++) {
  if (connectors[i]->has_tile &&
      connectors[i]->status == connector_status_connected)
   num_tiled_conns++;
 }

retry:
 for (i = 0; i < connector_count; i++) {
  struct drm_connector *connector = connectors[i];
  const char *mode_type;


  if (conn_configured & BIT_ULL(i))
   continue;

  if (enabled[i] == false) {
   conn_configured |= BIT_ULL(i);
   continue;
  }

  /* first pass over all the untiled connectors */
  if (tile_pass == 0 && connector->has_tile)
   continue;

  if (tile_pass == 1) {
   if (connector->tile_h_loc != 0 ||
       connector->tile_v_loc != 0)
    continue;

  } else {
   if (connector->tile_h_loc != tile_pass - 1 &&
       connector->tile_v_loc != tile_pass - 1)
   /* if this tile_pass doesn't cover any of the tiles - keep going */
    continue;

   /*
 * find the tile offsets for this pass - need to find
 * all tiles left and above
 */

   drm_client_get_tile_offsets(dev, connectors, connector_count,
          modes, offsets, i,
          connector->tile_h_loc, connector->tile_v_loc);
  }

  mode_type = "cmdline";
  mode_replace(dev, &modes[i],
        drm_connector_pick_cmdline_mode(connector));

  if (!modes[i]) {
   mode_type = "preferred";
   mode_replace(dev, &modes[i],
         drm_connector_preferred_mode(connector, width, height));
  }

  if (!modes[i]) {
   mode_type = "first";
   mode_replace(dev, &modes[i],
         drm_connector_first_mode(connector));
  }

  /*
 * In case of tiled mode if all tiles not present fallback to
 * first available non tiled mode.
 * After all tiles are present, try to find the tiled mode
 * for all and if tiled mode not present due to fbcon size
 * limitations, use first non tiled mode only for
 * tile 0,0 and set to no mode for all other tiles.
 */

  if (connector->has_tile) {
   if (num_tiled_conns <
       connector->num_h_tile * connector->num_v_tile ||
       (connector->tile_h_loc == 0 &&
        connector->tile_v_loc == 0 &&
        !drm_connector_get_tiled_mode(connector))) {
    mode_type = "non tiled";
    mode_replace(dev, &modes[i],
          drm_connector_fallback_non_tiled_mode(connector));
   } else {
    mode_type = "tiled";
    mode_replace(dev, &modes[i],
          drm_connector_get_tiled_mode(connector));
   }
  }

  if (modes[i])
   drm_dbg_kms(dev, "[CONNECTOR:%d:%s] found %s mode: %s\n",
        connector->base.id, connector->name,
        mode_type, modes[i]->name);
  else
   drm_dbg_kms(dev, "[CONNECTOR:%d:%s] no mode found\n",
        connector->base.id, connector->name);

  conn_configured |= BIT_ULL(i);
 }

 if ((conn_configured & mask) != mask) {
  tile_pass++;
  goto retry;
 }
 return true;
}

static bool connector_has_possible_crtc(struct drm_connector *connector,
     struct drm_crtc *crtc)
{
 struct drm_encoder *encoder;

 drm_connector_for_each_possible_encoder(connector, encoder) {
  if (encoder->possible_crtcs & drm_crtc_mask(crtc))
   return true;
 }

 return false;
}

static int drm_client_pick_crtcs(struct drm_client_dev *client,
     struct drm_connector *connectors[],
     unsigned int connector_count,
     struct drm_crtc *best_crtcs[],
     const struct drm_display_mode *modes[],
     int n, int width, int height)
{
 struct drm_device *dev = client->dev;
 struct drm_connector *connector;
 int my_score, best_score, score;
 struct drm_crtc **crtcs;
 struct drm_mode_set *modeset;

 if (n == connector_count)
  return 0;

 connector = connectors[n];

 best_crtcs[n] = NULL;
 best_score = drm_client_pick_crtcs(client, connectors, connector_count,
        best_crtcs, modes, n + 1, width, height);
 if (modes[n] == NULL)
  return best_score;

 crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
 if (!crtcs)
  return best_score;

 my_score = 1;
 if (connector->status == connector_status_connected)
  my_score++;
 if (connector->cmdline_mode.specified)
  my_score++;
 if (drm_connector_preferred_mode(connector, width, height))
  my_score++;

 /*
 * select a crtc for this connector and then attempt to configure
 * remaining connectors
 */

 drm_client_for_each_modeset(modeset, client) {
  struct drm_crtc *crtc = modeset->crtc;
  int o;

  if (!connector_has_possible_crtc(connector, crtc))
   continue;

  for (o = 0; o < n; o++)
   if (best_crtcs[o] == crtc)
    break;

  if (o < n) {
   /* ignore cloning unless only a single crtc */
   if (dev->mode_config.num_crtc > 1)
    continue;

   if (!drm_mode_equal(modes[o], modes[n]))
    continue;
  }

  crtcs[n] = crtc;
  memcpy(crtcs, best_crtcs, n * sizeof(*crtcs));
  score = my_score + drm_client_pick_crtcs(client, connectors, connector_count,
        crtcs, modes, n + 1, width, height);
  if (score > best_score) {
   best_score = score;
   memcpy(best_crtcs, crtcs, connector_count * sizeof(*crtcs));
  }
 }

 kfree(crtcs);
 return best_score;
}

/* Try to read the BIOS display configuration and use it for the initial config */
static bool drm_client_firmware_config(struct drm_client_dev *client,
           struct drm_connector *connectors[],
           unsigned int connector_count,
           struct drm_crtc *crtcs[],
           const struct drm_display_mode *modes[],
           struct drm_client_offset offsets[],
           bool enabled[], int width, int height)
{
 const int count = min_t(unsigned int, connector_count, BITS_PER_LONG);
 unsigned long conn_configured, conn_seq, mask;
 struct drm_device *dev = client->dev;
 int i;
 bool *save_enabled;
 bool fallback = true, ret = true;
 int num_connectors_enabled = 0;
 int num_connectors_detected = 0;
 int num_tiled_conns = 0;
 struct drm_modeset_acquire_ctx ctx;

 if (!drm_drv_uses_atomic_modeset(dev))
  return false;

 if (drm_WARN_ON(dev, count <= 0))
  return false;

 save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL);
 if (!save_enabled)
  return false;

 drm_modeset_acquire_init(&ctx, 0);

 while (drm_modeset_lock_all_ctx(dev, &ctx) != 0)
  drm_modeset_backoff(&ctx);

 memcpy(save_enabled, enabled, count);
 mask = GENMASK(count - 1, 0);
 conn_configured = 0;
 for (i = 0; i < count; i++) {
  if (connectors[i]->has_tile &&
      connectors[i]->status == connector_status_connected)
   num_tiled_conns++;
 }
retry:
 conn_seq = conn_configured;
 for (i = 0; i < count; i++) {
  struct drm_connector *connector = connectors[i];
  struct drm_encoder *encoder;
  struct drm_crtc *crtc;
  const char *mode_type;
  int j;

  if (conn_configured & BIT(i))
   continue;

  if (conn_seq == 0 && !connector->has_tile)
   continue;

  if (connector->status == connector_status_connected)
   num_connectors_detected++;

  if (!enabled[i]) {
   drm_dbg_kms(dev, "[CONNECTOR:%d:%s] not enabled, skipping\n",
        connector->base.id, connector->name);
   conn_configured |= BIT(i);
   continue;
  }

  if (connector->force == DRM_FORCE_OFF) {
   drm_dbg_kms(dev, "[CONNECTOR:%d:%s] disabled by user, skipping\n",
        connector->base.id, connector->name);
   enabled[i] = false;
   continue;
  }

  encoder = connector->state->best_encoder;
  if (!encoder || drm_WARN_ON(dev, !connector->state->crtc)) {
   if (connector->force > DRM_FORCE_OFF)
    goto bail;

   drm_dbg_kms(dev, "[CONNECTOR:%d:%s] has no encoder or crtc, skipping\n",
        connector->base.id, connector->name);
   enabled[i] = false;
   conn_configured |= BIT(i);
   continue;
  }

  num_connectors_enabled++;

  crtc = connector->state->crtc;

  /*
 * Make sure we're not trying to drive multiple connectors
 * with a single CRTC, since our cloning support may not
 * match the BIOS.
 */

  for (j = 0; j < count; j++) {
   if (crtcs[j] == crtc) {
    drm_dbg_kms(dev, "[CONNECTOR:%d:%s] fallback: cloned configuration\n",
         connector->base.id, connector->name);
    goto bail;
   }
  }

  mode_type = "cmdline";
  mode_replace(dev, &modes[i],
        drm_connector_pick_cmdline_mode(connector));

  if (!modes[i]) {
   mode_type = "preferred";
   mode_replace(dev, &modes[i],
         drm_connector_preferred_mode(connector, width, height));
  }

  if (!modes[i]) {
   mode_type = "first";
   mode_replace(dev, &modes[i],
         drm_connector_first_mode(connector));
  }

  /* last resort: use current mode */
  if (!modes[i]) {
   mode_type = "current";
   mode_replace(dev, &modes[i],
         &crtc->state->mode);
  }

  /*
 * In case of tiled modes, if all tiles are not present
 * then fallback to a non tiled mode.
 */

  if (connector->has_tile &&
      num_tiled_conns < connector->num_h_tile * connector->num_v_tile) {
   mode_type = "non tiled";
   mode_replace(dev, &modes[i],
         drm_connector_fallback_non_tiled_mode(connector));
  }
  crtcs[i] = crtc;

  drm_dbg_kms(dev, "[CONNECTOR::%d:%s] on [CRTC:%d:%s] using %s mode: %s\n",
       connector->base.id, connector->name,
       crtc->base.id, crtc->name,
       mode_type, modes[i]->name);

  fallback = false;
  conn_configured |= BIT(i);
 }

 if ((conn_configured & mask) != mask && conn_configured != conn_seq)
  goto retry;

 for (i = 0; i < count; i++) {
  struct drm_connector *connector = connectors[i];

  if (connector->has_tile)
   drm_client_get_tile_offsets(dev, connectors, connector_count,
          modes, offsets, i,
          connector->tile_h_loc, connector->tile_v_loc);
 }

 /*
 * If the BIOS didn't enable everything it could, fall back to have the
 * same user experiencing of lighting up as much as possible like the
 * fbdev helper library.
 */

 if (num_connectors_enabled != num_connectors_detected &&
     num_connectors_enabled < dev->mode_config.num_crtc) {
  drm_dbg_kms(dev, "fallback: Not all outputs enabled\n");
  drm_dbg_kms(dev, "Enabled: %i, detected: %i\n",
       num_connectors_enabled, num_connectors_detected);
  fallback = true;
 }

 if (fallback) {
bail:
  drm_dbg_kms(dev, "Not using firmware configuration\n");
  memcpy(enabled, save_enabled, count);
  ret = false;
 }

 drm_modeset_drop_locks(&ctx);
 drm_modeset_acquire_fini(&ctx);

 kfree(save_enabled);
 return ret;
}

/**
 * drm_client_modeset_probe() - Probe for displays
 * @client: DRM client
 * @width: Maximum display mode width (optional)
 * @height: Maximum display mode height (optional)
 *
 * This function sets up display pipelines for enabled connectors and stores the
 * config in the client's modeset array.
 *
 * Returns:
 * Zero on success or negative error code on failure.
 */

int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int height)
{
 struct drm_connector *connector, **connectors = NULL;
 struct drm_connector_list_iter conn_iter;
 struct drm_device *dev = client->dev;
 unsigned int total_modes_count = 0;
 struct drm_client_offset *offsets;
 unsigned int connector_count = 0;
 const struct drm_display_mode **modes;
 struct drm_crtc **crtcs;
 int i, ret = 0;
 bool *enabled;

 drm_dbg_kms(dev, "\n");

 if (!width)
  width = dev->mode_config.max_width;
 if (!height)
  height = dev->mode_config.max_height;

 drm_connector_list_iter_begin(dev, &conn_iter);
 drm_client_for_each_connector_iter(connector, &conn_iter) {
  struct drm_connector **tmp;

  tmp = krealloc(connectors, (connector_count + 1) * sizeof(*connectors), GFP_KERNEL);
  if (!tmp) {
   ret = -ENOMEM;
   goto free_connectors;
  }

  connectors = tmp;
  drm_connector_get(connector);
  connectors[connector_count++] = connector;
 }
 drm_connector_list_iter_end(&conn_iter);

 if (!connector_count)
  return 0;

 crtcs = kcalloc(connector_count, sizeof(*crtcs), GFP_KERNEL);
 modes = kcalloc(connector_count, sizeof(*modes), GFP_KERNEL);
 offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL);
 enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL);
 if (!crtcs || !modes || !enabled || !offsets) {
  ret = -ENOMEM;
  goto out;
 }

 mutex_lock(&client->modeset_mutex);

 mutex_lock(&dev->mode_config.mutex);
 for (i = 0; i < connector_count; i++)
  total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height);
 if (!total_modes_count)
  drm_dbg_kms(dev, "No connectors reported connected with modes\n");
 drm_client_connectors_enabled(connectors, connector_count, enabled);

 if (!drm_client_firmware_config(client, connectors, connector_count, crtcs,
     modes, offsets, enabled, width, height)) {
  modes_destroy(dev, modes, connector_count);
  memset(crtcs, 0, connector_count * sizeof(*crtcs));
  memset(offsets, 0, connector_count * sizeof(*offsets));

  if (!drm_client_target_cloned(dev, connectors, connector_count, modes,
           offsets, enabled, width, height) &&
      !drm_client_target_preferred(dev, connectors, connector_count, modes,
       offsets, enabled, width, height))
   drm_err(dev, "Unable to find initial modes\n");

  drm_dbg_kms(dev, "picking CRTCs for %dx%d config\n",
       width, height);

  drm_client_pick_crtcs(client, connectors, connector_count,
          crtcs, modes, 0, width, height);
 }

 mutex_unlock(&dev->mode_config.mutex);

 drm_client_modeset_release(client);

 for (i = 0; i < connector_count; i++) {
  const struct drm_display_mode *mode = modes[i];
  struct drm_crtc *crtc = crtcs[i];
  struct drm_client_offset *offset = &offsets[i];

  if (mode && crtc) {
   struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc);
   struct drm_connector *connector = connectors[i];

   drm_dbg_kms(dev, "[CRTC:%d:%s] desired mode %s set (%d,%d)\n",
        crtc->base.id, crtc->name,
        mode->name, offset->x, offset->y);

   if (drm_WARN_ON_ONCE(dev, modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS ||
          (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) {
    ret = -EINVAL;
    break;
   }

   drm_mode_destroy(dev, modeset->mode);
   modeset->mode = drm_mode_duplicate(dev, mode);
   if (!modeset->mode) {
    ret = -ENOMEM;
    break;
   }

   drm_connector_get(connector);
   modeset->connectors[modeset->num_connectors++] = connector;
   modeset->x = offset->x;
   modeset->y = offset->y;
  }
 }

 mutex_unlock(&client->modeset_mutex);
out:
 kfree(crtcs);
 modes_destroy(dev, modes, connector_count);
 kfree(modes);
 kfree(offsets);
 kfree(enabled);
free_connectors:
 for (i = 0; i < connector_count; i++)
  drm_connector_put(connectors[i]);
 kfree(connectors);

 return ret;
}
EXPORT_SYMBOL(drm_client_modeset_probe);

/**
 * drm_client_rotation() - Check the initial rotation value
 * @modeset: DRM modeset
 * @rotation: Returned rotation value
 *
 * This function checks if the primary plane in @modeset can hw rotate
 * to match the rotation needed on its connector.
 *
 * Note: Currently only 0 and 180 degrees are supported.
 *
 * Return:
 * True if the plane can do the rotation, false otherwise.
 */

bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation)
{
 struct drm_connector *connector = modeset->connectors[0];
 struct drm_plane *plane = modeset->crtc->primary;
 struct drm_cmdline_mode *cmdline;
 u64 valid_mask = 0;
 int i;

 if (!modeset->num_connectors)
  return false;

 switch (connector->display_info.panel_orientation) {
 case DRM_MODE_PANEL_ORIENTATION_BOTTOM_UP:
  *rotation = DRM_MODE_ROTATE_180;
  break;
 case DRM_MODE_PANEL_ORIENTATION_LEFT_UP:
  *rotation = DRM_MODE_ROTATE_90;
  break;
 case DRM_MODE_PANEL_ORIENTATION_RIGHT_UP:
  *rotation = DRM_MODE_ROTATE_270;
  break;
 default:
  *rotation = DRM_MODE_ROTATE_0;
 }

 /**
 * The panel already defined the default rotation
 * through its orientation. Whatever has been provided
 * on the command line needs to be added to that.
 *
 * Unfortunately, the rotations are at different bit
 * indices, so the math to add them up are not as
 * trivial as they could.
 *
 * Reflections on the other hand are pretty trivial to deal with, a
 * simple XOR between the two handle the addition nicely.
 */

 cmdline = &connector->cmdline_mode;
 if (cmdline->specified && cmdline->rotation_reflection) {
  unsigned int cmdline_rest, panel_rest;
  unsigned int cmdline_rot, panel_rot;
  unsigned int sum_rot, sum_rest;

  panel_rot = ilog2(*rotation & DRM_MODE_ROTATE_MASK);
  cmdline_rot = ilog2(cmdline->rotation_reflection & DRM_MODE_ROTATE_MASK);
  sum_rot = (panel_rot + cmdline_rot) % 4;

  panel_rest = *rotation & ~DRM_MODE_ROTATE_MASK;
  cmdline_rest = cmdline->rotation_reflection & ~DRM_MODE_ROTATE_MASK;
  sum_rest = panel_rest ^ cmdline_rest;

  *rotation = (1 << sum_rot) | sum_rest;
 }

 /*
 * TODO: support 90 / 270 degree hardware rotation,
 * depending on the hardware this may require the framebuffer
 * to be in a specific tiling format.
 */

 if (((*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_0 &&
      (*rotation & DRM_MODE_ROTATE_MASK) != DRM_MODE_ROTATE_180) ||
     !plane->rotation_property)
  return false;

 for (i = 0; i < plane->rotation_property->num_values; i++)
  valid_mask |= (1ULL << plane->rotation_property->values[i]);

 if (!(*rotation & valid_mask))
  return false;

 return true;
}
EXPORT_SYMBOL(drm_client_rotation);

static int drm_client_modeset_commit_atomic(struct drm_client_dev *client, bool activebool check)
{
 struct drm_device *dev = client->dev;
 struct drm_plane *plane;
 struct drm_atomic_state *state;
 struct drm_modeset_acquire_ctx ctx;
 struct drm_mode_set *mode_set;
 int ret;

 drm_modeset_acquire_init(&ctx, 0);

 state = drm_atomic_state_alloc(dev);
 if (!state) {
  ret = -ENOMEM;
  goto out_ctx;
 }

 state->acquire_ctx = &ctx;
retry:
 drm_for_each_plane(plane, dev) {
  struct drm_plane_state *plane_state;

  plane_state = drm_atomic_get_plane_state(state, plane);
  if (IS_ERR(plane_state)) {
   ret = PTR_ERR(plane_state);
   goto out_state;
  }

  plane_state->rotation = DRM_MODE_ROTATE_0;

  /* disable non-primary: */
  if (plane->type == DRM_PLANE_TYPE_PRIMARY)
   continue;

  ret = __drm_atomic_helper_disable_plane(plane, plane_state);
  if (ret != 0)
   goto out_state;
 }

 drm_client_for_each_modeset(mode_set, client) {
  struct drm_plane *primary = mode_set->crtc->primary;
  unsigned int rotation;

  if (drm_client_rotation(mode_set, &rotation)) {
   struct drm_plane_state *plane_state;

   /* Cannot fail as we've already gotten the plane state above */
   plane_state = drm_atomic_get_new_plane_state(state, primary);
   plane_state->rotation = rotation;
  }

  ret = __drm_atomic_helper_set_config(mode_set, state);
  if (ret != 0)
   goto out_state;

  /*
 * __drm_atomic_helper_set_config() sets active when a
 * mode is set, unconditionally clear it if we force DPMS off
 */

  if (!active) {
   struct drm_crtc *crtc = mode_set->crtc;
   struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);

   crtc_state->active = false;
  }
 }

 if (check)
  ret = drm_atomic_check_only(state);
 else
  ret = drm_atomic_commit(state);

out_state:
 if (ret == -EDEADLK)
  goto backoff;

 drm_atomic_state_put(state);
out_ctx:
 drm_modeset_drop_locks(&ctx);
 drm_modeset_acquire_fini(&ctx);

 return ret;

backoff:
 drm_atomic_state_clear(state);
 drm_modeset_backoff(&ctx);

 goto retry;
}

static int drm_client_modeset_commit_legacy(struct drm_client_dev *client)
{
 struct drm_device *dev = client->dev;
 struct drm_mode_set *mode_set;
 struct drm_plane *plane;
 int ret = 0;

 drm_modeset_lock_all(dev);
 drm_for_each_plane(plane, dev) {
  if (plane->type != DRM_PLANE_TYPE_PRIMARY)
   drm_plane_force_disable(plane);

  if (plane->rotation_property)
   drm_mode_plane_set_obj_prop(plane,
          plane->rotation_property,
          DRM_MODE_ROTATE_0);
 }

 drm_client_for_each_modeset(mode_set, client) {
  struct drm_crtc *crtc = mode_set->crtc;

  if (crtc->funcs->cursor_set2) {
   ret = crtc->funcs->cursor_set2(crtc, NULL, 0, 0, 0, 0, 0);
   if (ret)
    goto out;
  } else if (crtc->funcs->cursor_set) {
   ret = crtc->funcs->cursor_set(crtc, NULL, 0, 0, 0);
   if (ret)
    goto out;
  }

  ret = drm_mode_set_config_internal(mode_set);
  if (ret)
   goto out;
 }
out:
 drm_modeset_unlock_all(dev);

 return ret;
}

/**
 * drm_client_modeset_check() - Check modeset configuration
 * @client: DRM client
 *
 * Check modeset configuration.
 *
 * Returns:
 * Zero on success or negative error code on failure.
 */

int drm_client_modeset_check(struct drm_client_dev *client)
{
 int ret;

 if (!drm_drv_uses_atomic_modeset(client->dev))
  return 0;

 mutex_lock(&client->modeset_mutex);
 ret = drm_client_modeset_commit_atomic(client, truetrue);
 mutex_unlock(&client->modeset_mutex);

 return ret;
}
EXPORT_SYMBOL(drm_client_modeset_check);

/**
 * drm_client_modeset_commit_locked() - Force commit CRTC configuration
 * @client: DRM client
 *
 * Commit modeset configuration to crtcs without checking if there is a DRM
 * master. The assumption is that the caller already holds an internal DRM
 * master reference acquired with drm_master_internal_acquire().
 *
 * Returns:
 * Zero on success or negative error code on failure.
 */

int drm_client_modeset_commit_locked(struct drm_client_dev *client)
{
 struct drm_device *dev = client->dev;
 int ret;

 mutex_lock(&client->modeset_mutex);
 if (drm_drv_uses_atomic_modeset(dev))
  ret = drm_client_modeset_commit_atomic(client, truefalse);
 else
  ret = drm_client_modeset_commit_legacy(client);
 mutex_unlock(&client->modeset_mutex);

 return ret;
}
EXPORT_SYMBOL(drm_client_modeset_commit_locked);

/**
 * drm_client_modeset_commit() - Commit CRTC configuration
 * @client: DRM client
 *
 * Commit modeset configuration to crtcs.
 *
 * Returns:
 * Zero on success or negative error code on failure.
 */

int drm_client_modeset_commit(struct drm_client_dev *client)
{
 struct drm_device *dev = client->dev;
 int ret;

 if (!drm_master_internal_acquire(dev))
  return -EBUSY;

 ret = drm_client_modeset_commit_locked(client);

 drm_master_internal_release(dev);

 return ret;
}
EXPORT_SYMBOL(drm_client_modeset_commit);

static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dpms_mode)
{
 struct drm_device *dev = client->dev;
 struct drm_connector *connector;
 struct drm_mode_set *modeset;
 struct drm_modeset_acquire_ctx ctx;
 int ret;

 DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 0, ret);
 drm_client_for_each_modeset(modeset, client) {
  int j;

  if (!modeset->crtc->enabled)
   continue;

  for (j = 0; j < modeset->num_connectors; j++) {
   connector = modeset->connectors[j];
   connector->funcs->dpms(connector, dpms_mode);
   drm_object_property_set_value(&connector->base,
    dev->mode_config.dpms_property, dpms_mode);
  }
 }
 DRM_MODESET_LOCK_ALL_END(dev, ctx, ret);
}

/**
 * drm_client_modeset_dpms() - Set DPMS mode
 * @client: DRM client
 * @mode: DPMS mode
 *
 * Note: For atomic drivers @mode is reduced to on/off.
 *
 * Returns:
 * Zero on success or negative error code on failure.
 */

int drm_client_modeset_dpms(struct drm_client_dev *client, int mode)
{
 struct drm_device *dev = client->dev;
 int ret = 0;

 if (!drm_master_internal_acquire(dev))
  return -EBUSY;

 mutex_lock(&client->modeset_mutex);
 if (drm_drv_uses_atomic_modeset(dev))
  ret = drm_client_modeset_commit_atomic(client, mode == DRM_MODE_DPMS_ON, false);
 else
  drm_client_modeset_dpms_legacy(client, mode);
 mutex_unlock(&client->modeset_mutex);

 drm_master_internal_release(dev);

 return ret;
}
EXPORT_SYMBOL(drm_client_modeset_dpms);

#ifdef CONFIG_DRM_KUNIT_TEST
#include "tests/drm_client_modeset_test.c"
#endif

Messung V0.5
C=97 H=89 G=93

¤ Dauer der Verarbeitung: 0.8 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge