diff options
Diffstat (limited to 'drivers/gpu/drm/drm_client_modeset.c')
| -rw-r--r-- | drivers/gpu/drm/drm_client_modeset.c | 397 |
1 files changed, 250 insertions, 147 deletions
diff --git a/drivers/gpu/drm/drm_client_modeset.c b/drivers/gpu/drm/drm_client_modeset.c index 1b12a3c201a3..fc4caf7da5fc 100644 --- a/drivers/gpu/drm/drm_client_modeset.c +++ b/drivers/gpu/drm/drm_client_modeset.c @@ -8,6 +8,8 @@ */ #include "drm/drm_modeset_lock.h" + +#include <linux/export.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/slab.h> @@ -39,7 +41,7 @@ int drm_client_modeset_create(struct drm_client_dev *client) unsigned int max_connector_count = 1; struct drm_mode_set *modeset; struct drm_crtc *crtc; - unsigned int i = 0; + int i = 0; /* Add terminating zero entry to enable index less iteration */ client->modesets = kcalloc(num_crtc + 1, sizeof(*client->modesets), GFP_KERNEL); @@ -73,9 +75,10 @@ err_free: static void drm_client_modeset_release(struct drm_client_dev *client) { struct drm_mode_set *modeset; - unsigned int i; drm_client_for_each_modeset(modeset, client) { + int i; + drm_mode_destroy(client->dev, modeset->mode); modeset->mode = NULL; modeset->fb = NULL; @@ -117,10 +120,10 @@ drm_client_find_modeset(struct drm_client_dev *client, struct drm_crtc *crtc) return NULL; } -static struct drm_display_mode * +static const struct drm_display_mode * drm_connector_get_tiled_mode(struct drm_connector *connector) { - struct drm_display_mode *mode; + const struct drm_display_mode *mode; list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay == connector->tile_h_size && @@ -130,10 +133,10 @@ drm_connector_get_tiled_mode(struct drm_connector *connector) return NULL; } -static struct drm_display_mode * +static const struct drm_display_mode * drm_connector_fallback_non_tiled_mode(struct drm_connector *connector) { - struct drm_display_mode *mode; + const struct drm_display_mode *mode; list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay == connector->tile_h_size && @@ -144,10 +147,10 @@ drm_connector_fallback_non_tiled_mode(struct drm_connector *connector) return NULL; } -static struct drm_display_mode * -drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int height) +static const struct drm_display_mode * +drm_connector_preferred_mode(struct drm_connector *connector, int width, int height) { - struct drm_display_mode *mode; + const struct drm_display_mode *mode; list_for_each_entry(mode, &connector->modes, head) { if (mode->hdisplay > width || @@ -159,10 +162,18 @@ drm_connector_has_preferred_mode(struct drm_connector *connector, int width, int return NULL; } -static struct drm_display_mode *drm_connector_pick_cmdline_mode(struct drm_connector *connector) +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) { - struct drm_cmdline_mode *cmdline_mode; - struct drm_display_mode *mode; + const struct drm_cmdline_mode *cmdline_mode; + const struct drm_display_mode *mode; bool prefer_non_interlace; /* @@ -231,9 +242,9 @@ static bool drm_connector_enabled(struct drm_connector *connector, bool strict) return enable; } -static void drm_client_connectors_enabled(struct drm_connector **connectors, +static void drm_client_connectors_enabled(struct drm_connector *connectors[], unsigned int connector_count, - bool *enabled) + bool enabled[]) { bool any_enabled = false; struct drm_connector *connector; @@ -242,8 +253,10 @@ static void drm_client_connectors_enabled(struct drm_connector **connectors, for (i = 0; i < connector_count; i++) { connector = connectors[i]; enabled[i] = drm_connector_enabled(connector, true); - DRM_DEBUG_KMS("connector %d enabled? %s\n", connector->base.id, - connector->display_info.non_desktop ? "non desktop" : str_yes_no(enabled[i])); + 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]; } @@ -255,16 +268,35 @@ static void drm_client_connectors_enabled(struct drm_connector **connectors, 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, + struct drm_connector *connectors[], unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) + const struct drm_display_mode *modes[], + struct drm_client_offset offsets[], + bool enabled[], int width, int height) { - int count, i, j; + int count, i; bool can_clone = false; - struct drm_display_mode *dmt_mode, *mode; + struct drm_display_mode *dmt_mode; /* only contemplate cloning in the single crtc case */ if (dev->mode_config.num_crtc > 1) @@ -283,9 +315,13 @@ static bool drm_client_target_cloned(struct drm_device *dev, /* 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; - modes[i] = drm_connector_pick_cmdline_mode(connectors[i]); + + mode_replace(dev, &modes[i], + drm_connector_pick_cmdline_mode(connectors[i])); if (!modes[i]) { can_clone = false; break; @@ -303,7 +339,7 @@ static bool drm_client_target_cloned(struct drm_device *dev, } if (can_clone) { - DRM_DEBUG_KMS("can clone using command line\n"); + drm_dbg_kms(dev, "can clone using command line\n"); return true; } @@ -311,7 +347,12 @@ static bool drm_client_target_cloned(struct drm_device *dev, 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; @@ -321,39 +362,43 @@ static bool drm_client_target_cloned(struct drm_device *dev, DRM_MODE_MATCH_CLOCK | DRM_MODE_MATCH_FLAGS | DRM_MODE_MATCH_3D_FLAGS)) - modes[i] = mode; + mode_replace(dev, &modes[i], mode); } if (!modes[i]) can_clone = false; } + drm_mode_destroy(dev, dmt_mode); if (can_clone) { - DRM_DEBUG_KMS("can clone using 1024x768\n"); + drm_dbg_kms(dev, "can clone using 1024x768\n"); return true; } - DRM_INFO("kms: can't enable cloning when we probably wanted to.\n"); +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_connector **connectors, +static int drm_client_get_tile_offsets(struct drm_device *dev, + struct drm_connector *connectors[], unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, + const struct drm_display_mode *modes[], + struct drm_client_offset offsets[], int idx, int h_idx, int v_idx) { - struct drm_connector *connector; int i; int hoffset = 0, voffset = 0; for (i = 0; i < connector_count; i++) { - connector = connectors[i]; + struct drm_connector *connector = connectors[i]; + if (!connector->has_tile) continue; if (!modes[i] && (h_idx || v_idx)) { - DRM_DEBUG_KMS("no modes for connector tiled %d %d\n", i, - connector->base.id); + 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) @@ -364,18 +409,18 @@ static int drm_client_get_tile_offsets(struct drm_connector **connectors, } offsets[idx].x = hoffset; offsets[idx].y = voffset; - DRM_DEBUG_KMS("returned %d %d for %d %d\n", hoffset, voffset, h_idx, v_idx); + 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_connector **connectors, +static bool drm_client_target_preferred(struct drm_device *dev, + struct drm_connector *connectors[], unsigned int connector_count, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) + 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; - struct drm_connector *connector; u64 conn_configured = 0; int tile_pass = 0; int num_tiled_conns = 0; @@ -389,7 +434,9 @@ static bool drm_client_target_preferred(struct drm_connector **connectors, retry: for (i = 0; i < connector_count; i++) { - connector = connectors[i]; + struct drm_connector *connector = connectors[i]; + const char *mode_type; + if (conn_configured & BIT_ULL(i)) continue; @@ -418,24 +465,27 @@ retry: * find the tile offsets for this pass - need to find * all tiles left and above */ - drm_client_get_tile_offsets(connectors, connector_count, modes, offsets, i, + drm_client_get_tile_offsets(dev, connectors, connector_count, + modes, offsets, i, connector->tile_h_loc, connector->tile_v_loc); } - DRM_DEBUG_KMS("looking for cmdline mode on connector %d\n", - connector->base.id); - /* got for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); + mode_type = "cmdline"; + mode_replace(dev, &modes[i], + drm_connector_pick_cmdline_mode(connector)); + if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %d %d\n", - connector->base.id, connector->tile_group ? connector->tile_group->id : 0); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); + mode_type = "preferred"; + mode_replace(dev, &modes[i], + drm_connector_preferred_mode(connector, width, height)); } - /* No preferred modes, pick one off the list */ - if (!modes[i] && !list_empty(&connector->modes)) { - list_for_each_entry(modes[i], &connector->modes, head) - break; + + 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. @@ -450,16 +500,24 @@ retry: (connector->tile_h_loc == 0 && connector->tile_v_loc == 0 && !drm_connector_get_tiled_mode(connector))) { - DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n", - connector->base.id); - modes[i] = drm_connector_fallback_non_tiled_mode(connector); + mode_type = "non tiled"; + mode_replace(dev, &modes[i], + drm_connector_fallback_non_tiled_mode(connector)); } else { - modes[i] = drm_connector_get_tiled_mode(connector); + mode_type = "tiled"; + mode_replace(dev, &modes[i], + drm_connector_get_tiled_mode(connector)); } } - DRM_DEBUG_KMS("found mode %s\n", modes[i] ? modes[i]->name : - "none"); + 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); } @@ -484,18 +542,17 @@ static bool connector_has_possible_crtc(struct drm_connector *connector, } static int drm_client_pick_crtcs(struct drm_client_dev *client, - struct drm_connector **connectors, + struct drm_connector *connectors[], unsigned int connector_count, - struct drm_crtc **best_crtcs, - struct drm_display_mode **modes, + 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, *crtc; + struct drm_crtc **crtcs; struct drm_mode_set *modeset; - int o; if (n == connector_count) return 0; @@ -517,7 +574,7 @@ static int drm_client_pick_crtcs(struct drm_client_dev *client, my_score++; if (connector->cmdline_mode.specified) my_score++; - if (drm_connector_has_preferred_mode(connector, width, height)) + if (drm_connector_preferred_mode(connector, width, height)) my_score++; /* @@ -525,7 +582,8 @@ static int drm_client_pick_crtcs(struct drm_client_dev *client, * remaining connectors */ drm_client_for_each_modeset(modeset, client) { - crtc = modeset->crtc; + struct drm_crtc *crtc = modeset->crtc; + int o; if (!connector_has_possible_crtc(connector, crtc)) continue; @@ -559,17 +617,17 @@ static int drm_client_pick_crtcs(struct drm_client_dev *client, /* 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, + struct drm_connector *connectors[], unsigned int connector_count, - struct drm_crtc **crtcs, - struct drm_display_mode **modes, - struct drm_client_offset *offsets, - bool *enabled, int width, int height) + 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, j; + int i; bool *save_enabled; bool fallback = true, ret = true; int num_connectors_enabled = 0; @@ -580,7 +638,7 @@ static bool drm_client_firmware_config(struct drm_client_dev *client, if (!drm_drv_uses_atomic_modeset(dev)) return false; - if (WARN_ON(count <= 0)) + if (drm_WARN_ON(dev, count <= 0)) return false; save_enabled = kcalloc(count, sizeof(bool), GFP_KERNEL); @@ -603,11 +661,11 @@ static bool drm_client_firmware_config(struct drm_client_dev *client, retry: conn_seq = conn_configured; for (i = 0; i < count; i++) { - struct drm_connector *connector; + struct drm_connector *connector = connectors[i]; struct drm_encoder *encoder; - struct drm_crtc *new_crtc; - - connector = connectors[i]; + struct drm_crtc *crtc; + const char *mode_type; + int j; if (conn_configured & BIT(i)) continue; @@ -619,26 +677,26 @@ retry: num_connectors_detected++; if (!enabled[i]) { - DRM_DEBUG_KMS("connector %s not enabled, skipping\n", - connector->name); + 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_DEBUG_KMS("connector %s is disabled by user, skipping\n", - connector->name); + 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 || WARN_ON(!connector->state->crtc)) { + if (!encoder || drm_WARN_ON(dev, !connector->state->crtc)) { if (connector->force > DRM_FORCE_OFF) goto bail; - DRM_DEBUG_KMS("connector %s has no encoder or crtc, skipping\n", - connector->name); + 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; @@ -646,7 +704,7 @@ retry: num_connectors_enabled++; - new_crtc = connector->state->crtc; + crtc = connector->state->crtc; /* * Make sure we're not trying to drive multiple connectors @@ -654,69 +712,52 @@ retry: * match the BIOS. */ for (j = 0; j < count; j++) { - if (crtcs[j] == new_crtc) { - DRM_DEBUG_KMS("fallback: cloned configuration\n"); + if (crtcs[j] == crtc) { + drm_dbg_kms(dev, "[CONNECTOR:%d:%s] fallback: cloned configuration\n", + connector->base.id, connector->name); goto bail; } } - DRM_DEBUG_KMS("looking for cmdline mode on connector %s\n", - connector->name); + mode_type = "cmdline"; + mode_replace(dev, &modes[i], + drm_connector_pick_cmdline_mode(connector)); - /* go for command line mode first */ - modes[i] = drm_connector_pick_cmdline_mode(connector); - - /* try for preferred next */ if (!modes[i]) { - DRM_DEBUG_KMS("looking for preferred mode on connector %s %d\n", - connector->name, connector->has_tile); - modes[i] = drm_connector_has_preferred_mode(connector, width, height); + mode_type = "preferred"; + mode_replace(dev, &modes[i], + drm_connector_preferred_mode(connector, width, height)); } - /* No preferred mode marked by the EDID? Are there any modes? */ - if (!modes[i] && !list_empty(&connector->modes)) { - DRM_DEBUG_KMS("using first mode listed on connector %s\n", - connector->name); - modes[i] = list_first_entry(&connector->modes, - struct drm_display_mode, - head); + if (!modes[i]) { + mode_type = "first"; + mode_replace(dev, &modes[i], + drm_connector_first_mode(connector)); } /* last resort: use current mode */ if (!modes[i]) { - /* - * IMPORTANT: We want to use the adjusted mode (i.e. - * after the panel fitter upscaling) as the initial - * config, not the input mode, which is what crtc->mode - * usually contains. But since our current - * code puts a mode derived from the post-pfit timings - * into crtc->mode this works out correctly. - * - * This is crtc->mode and not crtc->state->mode for the - * fastboot check to work correctly. - */ - DRM_DEBUG_KMS("looking for current mode on connector %s\n", - connector->name); - modes[i] = &connector->state->crtc->mode; + 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) { - DRM_DEBUG_KMS("Falling back to non tiled mode on Connector %d\n", - connector->base.id); - modes[i] = drm_connector_fallback_non_tiled_mode(connector); + mode_type = "non tiled"; + mode_replace(dev, &modes[i], + drm_connector_fallback_non_tiled_mode(connector)); } - crtcs[i] = new_crtc; + crtcs[i] = crtc; - DRM_DEBUG_KMS("connector %s on [CRTC:%d:%s]: %dx%d%s\n", - connector->name, - connector->state->crtc->base.id, - connector->state->crtc->name, - modes[i]->hdisplay, modes[i]->vdisplay, - modes[i]->flags & DRM_MODE_FLAG_INTERLACE ? "i" : ""); + 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); @@ -725,6 +766,15 @@ retry: 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 @@ -732,15 +782,15 @@ retry: */ if (num_connectors_enabled != num_connectors_detected && num_connectors_enabled < dev->mode_config.num_crtc) { - DRM_DEBUG_KMS("fallback: Not all outputs enabled\n"); - DRM_DEBUG_KMS("Enabled: %i, detected: %i\n", num_connectors_enabled, - num_connectors_detected); + 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_DEBUG_KMS("Not using firmware configuration\n"); + drm_dbg_kms(dev, "Not using firmware configuration\n"); memcpy(enabled, save_enabled, count); ret = false; } @@ -772,12 +822,12 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, unsigned int total_modes_count = 0; struct drm_client_offset *offsets; unsigned int connector_count = 0; - struct drm_display_mode **modes; + const struct drm_display_mode **modes; struct drm_crtc **crtcs; int i, ret = 0; bool *enabled; - DRM_DEBUG_KMS("\n"); + drm_dbg_kms(dev, "\n"); if (!width) width = dev->mode_config.max_width; @@ -808,7 +858,6 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, offsets = kcalloc(connector_count, sizeof(*offsets), GFP_KERNEL); enabled = kcalloc(connector_count, sizeof(bool), GFP_KERNEL); if (!crtcs || !modes || !enabled || !offsets) { - DRM_ERROR("Memory allocation failed\n"); ret = -ENOMEM; goto out; } @@ -819,33 +868,34 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, for (i = 0; i < connector_count; i++) total_modes_count += connectors[i]->funcs->fill_modes(connectors[i], width, height); if (!total_modes_count) - DRM_DEBUG_KMS("No connectors reported connected with modes\n"); + 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)) { - memset(modes, 0, connector_count * sizeof(*modes)); + 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(connectors, connector_count, modes, + !drm_client_target_preferred(dev, connectors, connector_count, modes, offsets, enabled, width, height)) - DRM_ERROR("Unable to find initial modes\n"); + drm_err(dev, "Unable to find initial modes\n"); - DRM_DEBUG_KMS("picking CRTCs for %dx%d config\n", - width, height); + 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++) { - struct drm_display_mode *mode = modes[i]; + const struct drm_display_mode *mode = modes[i]; struct drm_crtc *crtc = crtcs[i]; struct drm_client_offset *offset = &offsets[i]; @@ -853,16 +903,23 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, struct drm_mode_set *modeset = drm_client_find_modeset(client, crtc); struct drm_connector *connector = connectors[i]; - DRM_DEBUG_KMS("desired mode %s set on crtc %d (%d,%d)\n", - mode->name, crtc->base.id, offset->x, offset->y); + 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 (WARN_ON_ONCE(modeset->num_connectors == DRM_CLIENT_MAX_CLONED_CONNECTORS || - (dev->mode_config.num_crtc > 1 && modeset->num_connectors == 1))) { + 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; @@ -873,6 +930,7 @@ int drm_client_modeset_probe(struct drm_client_dev *client, unsigned int width, mutex_unlock(&client->modeset_mutex); out: kfree(crtcs); + modes_destroy(dev, modes, connector_count); kfree(modes); kfree(offsets); kfree(enabled); @@ -904,7 +962,7 @@ bool drm_client_rotation(struct drm_mode_set *modeset, unsigned int *rotation) struct drm_plane *plane = modeset->crtc->primary; struct drm_cmdline_mode *cmdline; u64 valid_mask = 0; - unsigned int i; + int i; if (!modeset->num_connectors) return false; @@ -1185,11 +1243,12 @@ static void drm_client_modeset_dpms_legacy(struct drm_client_dev *client, int dp struct drm_connector *connector; struct drm_mode_set *modeset; struct drm_modeset_acquire_ctx ctx; - int j; 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; @@ -1234,6 +1293,50 @@ int drm_client_modeset_dpms(struct drm_client_dev *client, int mode) } EXPORT_SYMBOL(drm_client_modeset_dpms); +/** + * drm_client_modeset_wait_for_vblank() - Wait for the next VBLANK to occur + * @client: DRM client + * @crtc_index: The ndex of the CRTC to wait on + * + * Block the caller until the given CRTC has seen a VBLANK. Do nothing + * if the CRTC is disabled. If there's another DRM master present, fail + * with -EBUSY. + * + * Returns: + * 0 on success, or negative error code otherwise. + */ +int drm_client_modeset_wait_for_vblank(struct drm_client_dev *client, unsigned int crtc_index) +{ + struct drm_device *dev = client->dev; + struct drm_crtc *crtc; + int ret; + + /* + * Rate-limit update frequency to vblank. If there's a DRM master + * present, it could interfere while we're waiting for the vblank + * event. Don't wait in this case. + */ + if (!drm_master_internal_acquire(dev)) + return -EBUSY; + + crtc = client->modesets[crtc_index].crtc; + + /* + * Only wait for a vblank event if the CRTC is enabled, otherwise + * just don't do anything, not even report an error. + */ + ret = drm_crtc_vblank_get(crtc); + if (!ret) { + drm_crtc_wait_one_vblank(crtc); + drm_crtc_vblank_put(crtc); + } + + drm_master_internal_release(dev); + + return 0; +} +EXPORT_SYMBOL(drm_client_modeset_wait_for_vblank); + #ifdef CONFIG_DRM_KUNIT_TEST #include "tests/drm_client_modeset_test.c" #endif |
