162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2006-2008 Intel Corporation 362306a36Sopenharmony_ci * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 462306a36Sopenharmony_ci * Copyright (c) 2008 Red Hat Inc. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * DRM core CRTC related functions 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Permission to use, copy, modify, distribute, and sell this software and its 962306a36Sopenharmony_ci * documentation for any purpose is hereby granted without fee, provided that 1062306a36Sopenharmony_ci * the above copyright notice appear in all copies and that both that copyright 1162306a36Sopenharmony_ci * notice and this permission notice appear in supporting documentation, and 1262306a36Sopenharmony_ci * that the name of the copyright holders not be used in advertising or 1362306a36Sopenharmony_ci * publicity pertaining to distribution of the software without specific, 1462306a36Sopenharmony_ci * written prior permission. The copyright holders make no representations 1562306a36Sopenharmony_ci * about the suitability of this software for any purpose. It is provided "as 1662306a36Sopenharmony_ci * is" without express or implied warranty. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 1962306a36Sopenharmony_ci * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 2062306a36Sopenharmony_ci * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 2162306a36Sopenharmony_ci * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 2262306a36Sopenharmony_ci * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 2362306a36Sopenharmony_ci * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 2462306a36Sopenharmony_ci * OF THIS SOFTWARE. 2562306a36Sopenharmony_ci * 2662306a36Sopenharmony_ci * Authors: 2762306a36Sopenharmony_ci * Keith Packard 2862306a36Sopenharmony_ci * Eric Anholt <eric@anholt.net> 2962306a36Sopenharmony_ci * Dave Airlie <airlied@linux.ie> 3062306a36Sopenharmony_ci * Jesse Barnes <jesse.barnes@intel.com> 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci#include <linux/ctype.h> 3362306a36Sopenharmony_ci#include <linux/list.h> 3462306a36Sopenharmony_ci#include <linux/slab.h> 3562306a36Sopenharmony_ci#include <linux/export.h> 3662306a36Sopenharmony_ci#include <linux/dma-fence.h> 3762306a36Sopenharmony_ci#include <linux/uaccess.h> 3862306a36Sopenharmony_ci#include <drm/drm_blend.h> 3962306a36Sopenharmony_ci#include <drm/drm_crtc.h> 4062306a36Sopenharmony_ci#include <drm/drm_edid.h> 4162306a36Sopenharmony_ci#include <drm/drm_fourcc.h> 4262306a36Sopenharmony_ci#include <drm/drm_framebuffer.h> 4362306a36Sopenharmony_ci#include <drm/drm_managed.h> 4462306a36Sopenharmony_ci#include <drm/drm_modeset_lock.h> 4562306a36Sopenharmony_ci#include <drm/drm_atomic.h> 4662306a36Sopenharmony_ci#include <drm/drm_auth.h> 4762306a36Sopenharmony_ci#include <drm/drm_debugfs_crc.h> 4862306a36Sopenharmony_ci#include <drm/drm_drv.h> 4962306a36Sopenharmony_ci#include <drm/drm_print.h> 5062306a36Sopenharmony_ci#include <drm/drm_file.h> 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci#include "drm_crtc_internal.h" 5362306a36Sopenharmony_ci#include "drm_internal.h" 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/** 5662306a36Sopenharmony_ci * DOC: overview 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * A CRTC represents the overall display pipeline. It receives pixel data from 5962306a36Sopenharmony_ci * &drm_plane and blends them together. The &drm_display_mode is also attached 6062306a36Sopenharmony_ci * to the CRTC, specifying display timings. On the output side the data is fed 6162306a36Sopenharmony_ci * to one or more &drm_encoder, which are then each connected to one 6262306a36Sopenharmony_ci * &drm_connector. 6362306a36Sopenharmony_ci * 6462306a36Sopenharmony_ci * To create a CRTC, a KMS drivers allocates and zeroes an instances of 6562306a36Sopenharmony_ci * &struct drm_crtc (possibly as part of a larger structure) and registers it 6662306a36Sopenharmony_ci * with a call to drm_crtc_init_with_planes(). 6762306a36Sopenharmony_ci * 6862306a36Sopenharmony_ci * The CRTC is also the entry point for legacy modeset operations, see 6962306a36Sopenharmony_ci * &drm_crtc_funcs.set_config, legacy plane operations, see 7062306a36Sopenharmony_ci * &drm_crtc_funcs.page_flip and &drm_crtc_funcs.cursor_set2, and other legacy 7162306a36Sopenharmony_ci * operations like &drm_crtc_funcs.gamma_set. For atomic drivers all these 7262306a36Sopenharmony_ci * features are controlled through &drm_property and 7362306a36Sopenharmony_ci * &drm_mode_config_funcs.atomic_check. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci/** 7762306a36Sopenharmony_ci * drm_crtc_from_index - find the registered CRTC at an index 7862306a36Sopenharmony_ci * @dev: DRM device 7962306a36Sopenharmony_ci * @idx: index of registered CRTC to find for 8062306a36Sopenharmony_ci * 8162306a36Sopenharmony_ci * Given a CRTC index, return the registered CRTC from DRM device's 8262306a36Sopenharmony_ci * list of CRTCs with matching index. This is the inverse of drm_crtc_index(). 8362306a36Sopenharmony_ci * It's useful in the vblank callbacks (like &drm_driver.enable_vblank or 8462306a36Sopenharmony_ci * &drm_driver.disable_vblank), since that still deals with indices instead 8562306a36Sopenharmony_ci * of pointers to &struct drm_crtc." 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_cistruct drm_crtc *drm_crtc_from_index(struct drm_device *dev, int idx) 8862306a36Sopenharmony_ci{ 8962306a36Sopenharmony_ci struct drm_crtc *crtc; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) 9262306a36Sopenharmony_ci if (idx == crtc->index) 9362306a36Sopenharmony_ci return crtc; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci return NULL; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_from_index); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ciint drm_crtc_force_disable(struct drm_crtc *crtc) 10062306a36Sopenharmony_ci{ 10162306a36Sopenharmony_ci struct drm_mode_set set = { 10262306a36Sopenharmony_ci .crtc = crtc, 10362306a36Sopenharmony_ci }; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(crtc->dev)); 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci return drm_mode_set_config_internal(&set); 10862306a36Sopenharmony_ci} 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic unsigned int drm_num_crtcs(struct drm_device *dev) 11162306a36Sopenharmony_ci{ 11262306a36Sopenharmony_ci unsigned int num = 0; 11362306a36Sopenharmony_ci struct drm_crtc *tmp; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci drm_for_each_crtc(tmp, dev) { 11662306a36Sopenharmony_ci num++; 11762306a36Sopenharmony_ci } 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci return num; 12062306a36Sopenharmony_ci} 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ciint drm_crtc_register_all(struct drm_device *dev) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci struct drm_crtc *crtc; 12562306a36Sopenharmony_ci int ret = 0; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 12862306a36Sopenharmony_ci drm_debugfs_crtc_add(crtc); 12962306a36Sopenharmony_ci 13062306a36Sopenharmony_ci if (crtc->funcs->late_register) 13162306a36Sopenharmony_ci ret = crtc->funcs->late_register(crtc); 13262306a36Sopenharmony_ci if (ret) 13362306a36Sopenharmony_ci return ret; 13462306a36Sopenharmony_ci } 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci return 0; 13762306a36Sopenharmony_ci} 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_civoid drm_crtc_unregister_all(struct drm_device *dev) 14062306a36Sopenharmony_ci{ 14162306a36Sopenharmony_ci struct drm_crtc *crtc; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci drm_for_each_crtc(crtc, dev) { 14462306a36Sopenharmony_ci if (crtc->funcs->early_unregister) 14562306a36Sopenharmony_ci crtc->funcs->early_unregister(crtc); 14662306a36Sopenharmony_ci drm_debugfs_crtc_remove(crtc); 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic int drm_crtc_crc_init(struct drm_crtc *crtc) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 15362306a36Sopenharmony_ci spin_lock_init(&crtc->crc.lock); 15462306a36Sopenharmony_ci init_waitqueue_head(&crtc->crc.wq); 15562306a36Sopenharmony_ci crtc->crc.source = kstrdup("auto", GFP_KERNEL); 15662306a36Sopenharmony_ci if (!crtc->crc.source) 15762306a36Sopenharmony_ci return -ENOMEM; 15862306a36Sopenharmony_ci#endif 15962306a36Sopenharmony_ci return 0; 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic void drm_crtc_crc_fini(struct drm_crtc *crtc) 16362306a36Sopenharmony_ci{ 16462306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_FS 16562306a36Sopenharmony_ci kfree(crtc->crc.source); 16662306a36Sopenharmony_ci#endif 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic const struct dma_fence_ops drm_crtc_fence_ops; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct drm_crtc *fence_to_crtc(struct dma_fence *fence) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci BUG_ON(fence->ops != &drm_crtc_fence_ops); 17462306a36Sopenharmony_ci return container_of(fence->lock, struct drm_crtc, fence_lock); 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic const char *drm_crtc_fence_get_driver_name(struct dma_fence *fence) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct drm_crtc *crtc = fence_to_crtc(fence); 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_ci return crtc->dev->driver->name; 18262306a36Sopenharmony_ci} 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_cistatic const char *drm_crtc_fence_get_timeline_name(struct dma_fence *fence) 18562306a36Sopenharmony_ci{ 18662306a36Sopenharmony_ci struct drm_crtc *crtc = fence_to_crtc(fence); 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_ci return crtc->timeline_name; 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic const struct dma_fence_ops drm_crtc_fence_ops = { 19262306a36Sopenharmony_ci .get_driver_name = drm_crtc_fence_get_driver_name, 19362306a36Sopenharmony_ci .get_timeline_name = drm_crtc_fence_get_timeline_name, 19462306a36Sopenharmony_ci}; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistruct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct dma_fence *fence; 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci fence = kzalloc(sizeof(*fence), GFP_KERNEL); 20162306a36Sopenharmony_ci if (!fence) 20262306a36Sopenharmony_ci return NULL; 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci dma_fence_init(fence, &drm_crtc_fence_ops, &crtc->fence_lock, 20562306a36Sopenharmony_ci crtc->fence_context, ++crtc->fence_seqno); 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci return fence; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci/** 21162306a36Sopenharmony_ci * DOC: standard CRTC properties 21262306a36Sopenharmony_ci * 21362306a36Sopenharmony_ci * DRM CRTCs have a few standardized properties: 21462306a36Sopenharmony_ci * 21562306a36Sopenharmony_ci * ACTIVE: 21662306a36Sopenharmony_ci * Atomic property for setting the power state of the CRTC. When set to 1 21762306a36Sopenharmony_ci * the CRTC will actively display content. When set to 0 the CRTC will be 21862306a36Sopenharmony_ci * powered off. There is no expectation that user-space will reset CRTC 21962306a36Sopenharmony_ci * resources like the mode and planes when setting ACTIVE to 0. 22062306a36Sopenharmony_ci * 22162306a36Sopenharmony_ci * User-space can rely on an ACTIVE change to 1 to never fail an atomic 22262306a36Sopenharmony_ci * test as long as no other property has changed. If a change to ACTIVE 22362306a36Sopenharmony_ci * fails an atomic test, this is a driver bug. For this reason setting 22462306a36Sopenharmony_ci * ACTIVE to 0 must not release internal resources (like reserved memory 22562306a36Sopenharmony_ci * bandwidth or clock generators). 22662306a36Sopenharmony_ci * 22762306a36Sopenharmony_ci * Note that the legacy DPMS property on connectors is internally routed 22862306a36Sopenharmony_ci * to control this property for atomic drivers. 22962306a36Sopenharmony_ci * MODE_ID: 23062306a36Sopenharmony_ci * Atomic property for setting the CRTC display timings. The value is the 23162306a36Sopenharmony_ci * ID of a blob containing the DRM mode info. To disable the CRTC, 23262306a36Sopenharmony_ci * user-space must set this property to 0. 23362306a36Sopenharmony_ci * 23462306a36Sopenharmony_ci * Setting MODE_ID to 0 will release reserved resources for the CRTC. 23562306a36Sopenharmony_ci * SCALING_FILTER: 23662306a36Sopenharmony_ci * Atomic property for setting the scaling filter for CRTC scaler 23762306a36Sopenharmony_ci * 23862306a36Sopenharmony_ci * The value of this property can be one of the following: 23962306a36Sopenharmony_ci * 24062306a36Sopenharmony_ci * Default: 24162306a36Sopenharmony_ci * Driver's default scaling filter 24262306a36Sopenharmony_ci * Nearest Neighbor: 24362306a36Sopenharmony_ci * Nearest Neighbor scaling filter 24462306a36Sopenharmony_ci */ 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci__printf(6, 0) 24762306a36Sopenharmony_cistatic int __drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, 24862306a36Sopenharmony_ci struct drm_plane *primary, 24962306a36Sopenharmony_ci struct drm_plane *cursor, 25062306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs, 25162306a36Sopenharmony_ci const char *name, va_list ap) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct drm_mode_config *config = &dev->mode_config; 25462306a36Sopenharmony_ci int ret; 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY); 25762306a36Sopenharmony_ci WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR); 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_ci /* crtc index is used with 32bit bitmasks */ 26062306a36Sopenharmony_ci if (WARN_ON(config->num_crtc >= 32)) 26162306a36Sopenharmony_ci return -EINVAL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(dev) && 26462306a36Sopenharmony_ci (!funcs->atomic_destroy_state || 26562306a36Sopenharmony_ci !funcs->atomic_duplicate_state)); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci crtc->dev = dev; 26862306a36Sopenharmony_ci crtc->funcs = funcs; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci INIT_LIST_HEAD(&crtc->commit_list); 27162306a36Sopenharmony_ci spin_lock_init(&crtc->commit_lock); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci drm_modeset_lock_init(&crtc->mutex); 27462306a36Sopenharmony_ci ret = drm_mode_object_add(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 27562306a36Sopenharmony_ci if (ret) 27662306a36Sopenharmony_ci return ret; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci if (name) { 27962306a36Sopenharmony_ci crtc->name = kvasprintf(GFP_KERNEL, name, ap); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", 28262306a36Sopenharmony_ci drm_num_crtcs(dev)); 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci if (!crtc->name) { 28562306a36Sopenharmony_ci drm_mode_object_unregister(dev, &crtc->base); 28662306a36Sopenharmony_ci return -ENOMEM; 28762306a36Sopenharmony_ci } 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ci crtc->fence_context = dma_fence_context_alloc(1); 29062306a36Sopenharmony_ci spin_lock_init(&crtc->fence_lock); 29162306a36Sopenharmony_ci snprintf(crtc->timeline_name, sizeof(crtc->timeline_name), 29262306a36Sopenharmony_ci "CRTC:%d-%s", crtc->base.id, crtc->name); 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci crtc->base.properties = &crtc->properties; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci list_add_tail(&crtc->head, &config->crtc_list); 29762306a36Sopenharmony_ci crtc->index = config->num_crtc++; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci crtc->primary = primary; 30062306a36Sopenharmony_ci crtc->cursor = cursor; 30162306a36Sopenharmony_ci if (primary && !primary->possible_crtcs) 30262306a36Sopenharmony_ci primary->possible_crtcs = drm_crtc_mask(crtc); 30362306a36Sopenharmony_ci if (cursor && !cursor->possible_crtcs) 30462306a36Sopenharmony_ci cursor->possible_crtcs = drm_crtc_mask(crtc); 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ret = drm_crtc_crc_init(crtc); 30762306a36Sopenharmony_ci if (ret) { 30862306a36Sopenharmony_ci drm_mode_object_unregister(dev, &crtc->base); 30962306a36Sopenharmony_ci return ret; 31062306a36Sopenharmony_ci } 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 31362306a36Sopenharmony_ci drm_object_attach_property(&crtc->base, config->prop_active, 0); 31462306a36Sopenharmony_ci drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); 31562306a36Sopenharmony_ci drm_object_attach_property(&crtc->base, 31662306a36Sopenharmony_ci config->prop_out_fence_ptr, 0); 31762306a36Sopenharmony_ci drm_object_attach_property(&crtc->base, 31862306a36Sopenharmony_ci config->prop_vrr_enabled, 0); 31962306a36Sopenharmony_ci } 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_ci/** 32562306a36Sopenharmony_ci * drm_crtc_init_with_planes - Initialise a new CRTC object with 32662306a36Sopenharmony_ci * specified primary and cursor planes. 32762306a36Sopenharmony_ci * @dev: DRM device 32862306a36Sopenharmony_ci * @crtc: CRTC object to init 32962306a36Sopenharmony_ci * @primary: Primary plane for CRTC 33062306a36Sopenharmony_ci * @cursor: Cursor plane for CRTC 33162306a36Sopenharmony_ci * @funcs: callbacks for the new CRTC 33262306a36Sopenharmony_ci * @name: printf style format string for the CRTC name, or NULL for default name 33362306a36Sopenharmony_ci * 33462306a36Sopenharmony_ci * Inits a new object created as base part of a driver crtc object. Drivers 33562306a36Sopenharmony_ci * should use this function instead of drm_crtc_init(), which is only provided 33662306a36Sopenharmony_ci * for backwards compatibility with drivers which do not yet support universal 33762306a36Sopenharmony_ci * planes). For really simple hardware which has only 1 plane look at 33862306a36Sopenharmony_ci * drm_simple_display_pipe_init() instead. 33962306a36Sopenharmony_ci * The &drm_crtc_funcs.destroy hook should call drm_crtc_cleanup() and kfree() 34062306a36Sopenharmony_ci * the crtc structure. The crtc structure should not be allocated with 34162306a36Sopenharmony_ci * devm_kzalloc(). 34262306a36Sopenharmony_ci * 34362306a36Sopenharmony_ci * The @primary and @cursor planes are only relevant for legacy uAPI, see 34462306a36Sopenharmony_ci * &drm_crtc.primary and &drm_crtc.cursor. 34562306a36Sopenharmony_ci * 34662306a36Sopenharmony_ci * Note: consider using drmm_crtc_alloc_with_planes() or 34762306a36Sopenharmony_ci * drmm_crtc_init_with_planes() instead of drm_crtc_init_with_planes() 34862306a36Sopenharmony_ci * to let the DRM managed resource infrastructure take care of cleanup 34962306a36Sopenharmony_ci * and deallocation. 35062306a36Sopenharmony_ci * 35162306a36Sopenharmony_ci * Returns: 35262306a36Sopenharmony_ci * Zero on success, error code on failure. 35362306a36Sopenharmony_ci */ 35462306a36Sopenharmony_ciint drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, 35562306a36Sopenharmony_ci struct drm_plane *primary, 35662306a36Sopenharmony_ci struct drm_plane *cursor, 35762306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs, 35862306a36Sopenharmony_ci const char *name, ...) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci va_list ap; 36162306a36Sopenharmony_ci int ret; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci WARN_ON(!funcs->destroy); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci va_start(ap, name); 36662306a36Sopenharmony_ci ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, 36762306a36Sopenharmony_ci name, ap); 36862306a36Sopenharmony_ci va_end(ap); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci return ret; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_init_with_planes); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_cistatic void drmm_crtc_init_with_planes_cleanup(struct drm_device *dev, 37562306a36Sopenharmony_ci void *ptr) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci struct drm_crtc *crtc = ptr; 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci drm_crtc_cleanup(crtc); 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci__printf(6, 0) 38362306a36Sopenharmony_cistatic int __drmm_crtc_init_with_planes(struct drm_device *dev, 38462306a36Sopenharmony_ci struct drm_crtc *crtc, 38562306a36Sopenharmony_ci struct drm_plane *primary, 38662306a36Sopenharmony_ci struct drm_plane *cursor, 38762306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs, 38862306a36Sopenharmony_ci const char *name, 38962306a36Sopenharmony_ci va_list args) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci int ret; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci drm_WARN_ON(dev, funcs && funcs->destroy); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = __drm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, 39662306a36Sopenharmony_ci name, args); 39762306a36Sopenharmony_ci if (ret) 39862306a36Sopenharmony_ci return ret; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ret = drmm_add_action_or_reset(dev, drmm_crtc_init_with_planes_cleanup, 40162306a36Sopenharmony_ci crtc); 40262306a36Sopenharmony_ci if (ret) 40362306a36Sopenharmony_ci return ret; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci return 0; 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci/** 40962306a36Sopenharmony_ci * drmm_crtc_init_with_planes - Initialise a new CRTC object with 41062306a36Sopenharmony_ci * specified primary and cursor planes. 41162306a36Sopenharmony_ci * @dev: DRM device 41262306a36Sopenharmony_ci * @crtc: CRTC object to init 41362306a36Sopenharmony_ci * @primary: Primary plane for CRTC 41462306a36Sopenharmony_ci * @cursor: Cursor plane for CRTC 41562306a36Sopenharmony_ci * @funcs: callbacks for the new CRTC 41662306a36Sopenharmony_ci * @name: printf style format string for the CRTC name, or NULL for default name 41762306a36Sopenharmony_ci * 41862306a36Sopenharmony_ci * Inits a new object created as base part of a driver crtc object. Drivers 41962306a36Sopenharmony_ci * should use this function instead of drm_crtc_init(), which is only provided 42062306a36Sopenharmony_ci * for backwards compatibility with drivers which do not yet support universal 42162306a36Sopenharmony_ci * planes). For really simple hardware which has only 1 plane look at 42262306a36Sopenharmony_ci * drm_simple_display_pipe_init() instead. 42362306a36Sopenharmony_ci * 42462306a36Sopenharmony_ci * Cleanup is automatically handled through registering 42562306a36Sopenharmony_ci * drmm_crtc_cleanup() with drmm_add_action(). The crtc structure should 42662306a36Sopenharmony_ci * be allocated with drmm_kzalloc(). 42762306a36Sopenharmony_ci * 42862306a36Sopenharmony_ci * The @drm_crtc_funcs.destroy hook must be NULL. 42962306a36Sopenharmony_ci * 43062306a36Sopenharmony_ci * The @primary and @cursor planes are only relevant for legacy uAPI, see 43162306a36Sopenharmony_ci * &drm_crtc.primary and &drm_crtc.cursor. 43262306a36Sopenharmony_ci * 43362306a36Sopenharmony_ci * Returns: 43462306a36Sopenharmony_ci * Zero on success, error code on failure. 43562306a36Sopenharmony_ci */ 43662306a36Sopenharmony_ciint drmm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, 43762306a36Sopenharmony_ci struct drm_plane *primary, 43862306a36Sopenharmony_ci struct drm_plane *cursor, 43962306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs, 44062306a36Sopenharmony_ci const char *name, ...) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci va_list ap; 44362306a36Sopenharmony_ci int ret; 44462306a36Sopenharmony_ci 44562306a36Sopenharmony_ci va_start(ap, name); 44662306a36Sopenharmony_ci ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, 44762306a36Sopenharmony_ci name, ap); 44862306a36Sopenharmony_ci va_end(ap); 44962306a36Sopenharmony_ci if (ret) 45062306a36Sopenharmony_ci return ret; 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ciEXPORT_SYMBOL(drmm_crtc_init_with_planes); 45562306a36Sopenharmony_ci 45662306a36Sopenharmony_civoid *__drmm_crtc_alloc_with_planes(struct drm_device *dev, 45762306a36Sopenharmony_ci size_t size, size_t offset, 45862306a36Sopenharmony_ci struct drm_plane *primary, 45962306a36Sopenharmony_ci struct drm_plane *cursor, 46062306a36Sopenharmony_ci const struct drm_crtc_funcs *funcs, 46162306a36Sopenharmony_ci const char *name, ...) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci void *container; 46462306a36Sopenharmony_ci struct drm_crtc *crtc; 46562306a36Sopenharmony_ci va_list ap; 46662306a36Sopenharmony_ci int ret; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (WARN_ON(!funcs || funcs->destroy)) 46962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci container = drmm_kzalloc(dev, size, GFP_KERNEL); 47262306a36Sopenharmony_ci if (!container) 47362306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci crtc = container + offset; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci va_start(ap, name); 47862306a36Sopenharmony_ci ret = __drmm_crtc_init_with_planes(dev, crtc, primary, cursor, funcs, 47962306a36Sopenharmony_ci name, ap); 48062306a36Sopenharmony_ci va_end(ap); 48162306a36Sopenharmony_ci if (ret) 48262306a36Sopenharmony_ci return ERR_PTR(ret); 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci return container; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ciEXPORT_SYMBOL(__drmm_crtc_alloc_with_planes); 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci/** 48962306a36Sopenharmony_ci * drm_crtc_cleanup - Clean up the core crtc usage 49062306a36Sopenharmony_ci * @crtc: CRTC to cleanup 49162306a36Sopenharmony_ci * 49262306a36Sopenharmony_ci * This function cleans up @crtc and removes it from the DRM mode setting 49362306a36Sopenharmony_ci * core. Note that the function does *not* free the crtc structure itself, 49462306a36Sopenharmony_ci * this is the responsibility of the caller. 49562306a36Sopenharmony_ci */ 49662306a36Sopenharmony_civoid drm_crtc_cleanup(struct drm_crtc *crtc) 49762306a36Sopenharmony_ci{ 49862306a36Sopenharmony_ci struct drm_device *dev = crtc->dev; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* Note that the crtc_list is considered to be static; should we 50162306a36Sopenharmony_ci * remove the drm_crtc at runtime we would have to decrement all 50262306a36Sopenharmony_ci * the indices on the drm_crtc after us in the crtc_list. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci drm_crtc_crc_fini(crtc); 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci kfree(crtc->gamma_store); 50862306a36Sopenharmony_ci crtc->gamma_store = NULL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci drm_modeset_lock_fini(&crtc->mutex); 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci drm_mode_object_unregister(dev, &crtc->base); 51362306a36Sopenharmony_ci list_del(&crtc->head); 51462306a36Sopenharmony_ci dev->mode_config.num_crtc--; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); 51762306a36Sopenharmony_ci if (crtc->state && crtc->funcs->atomic_destroy_state) 51862306a36Sopenharmony_ci crtc->funcs->atomic_destroy_state(crtc, crtc->state); 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci kfree(crtc->name); 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci memset(crtc, 0, sizeof(*crtc)); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_cleanup); 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci/** 52762306a36Sopenharmony_ci * drm_mode_getcrtc - get CRTC configuration 52862306a36Sopenharmony_ci * @dev: drm device for the ioctl 52962306a36Sopenharmony_ci * @data: data pointer for the ioctl 53062306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 53162306a36Sopenharmony_ci * 53262306a36Sopenharmony_ci * Construct a CRTC configuration structure to return to the user. 53362306a36Sopenharmony_ci * 53462306a36Sopenharmony_ci * Called by the user via ioctl. 53562306a36Sopenharmony_ci * 53662306a36Sopenharmony_ci * Returns: 53762306a36Sopenharmony_ci * Zero on success, negative errno on failure. 53862306a36Sopenharmony_ci */ 53962306a36Sopenharmony_ciint drm_mode_getcrtc(struct drm_device *dev, 54062306a36Sopenharmony_ci void *data, struct drm_file *file_priv) 54162306a36Sopenharmony_ci{ 54262306a36Sopenharmony_ci struct drm_mode_crtc *crtc_resp = data; 54362306a36Sopenharmony_ci struct drm_crtc *crtc; 54462306a36Sopenharmony_ci struct drm_plane *plane; 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 54762306a36Sopenharmony_ci return -EOPNOTSUPP; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci crtc = drm_crtc_find(dev, file_priv, crtc_resp->crtc_id); 55062306a36Sopenharmony_ci if (!crtc) 55162306a36Sopenharmony_ci return -ENOENT; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci plane = crtc->primary; 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci crtc_resp->gamma_size = crtc->gamma_size; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci drm_modeset_lock(&plane->mutex, NULL); 55862306a36Sopenharmony_ci if (plane->state && plane->state->fb) 55962306a36Sopenharmony_ci crtc_resp->fb_id = plane->state->fb->base.id; 56062306a36Sopenharmony_ci else if (!plane->state && plane->fb) 56162306a36Sopenharmony_ci crtc_resp->fb_id = plane->fb->base.id; 56262306a36Sopenharmony_ci else 56362306a36Sopenharmony_ci crtc_resp->fb_id = 0; 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci if (plane->state) { 56662306a36Sopenharmony_ci crtc_resp->x = plane->state->src_x >> 16; 56762306a36Sopenharmony_ci crtc_resp->y = plane->state->src_y >> 16; 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci drm_modeset_unlock(&plane->mutex); 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci drm_modeset_lock(&crtc->mutex, NULL); 57262306a36Sopenharmony_ci if (crtc->state) { 57362306a36Sopenharmony_ci if (crtc->state->enable) { 57462306a36Sopenharmony_ci drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); 57562306a36Sopenharmony_ci crtc_resp->mode_valid = 1; 57662306a36Sopenharmony_ci } else { 57762306a36Sopenharmony_ci crtc_resp->mode_valid = 0; 57862306a36Sopenharmony_ci } 57962306a36Sopenharmony_ci } else { 58062306a36Sopenharmony_ci crtc_resp->x = crtc->x; 58162306a36Sopenharmony_ci crtc_resp->y = crtc->y; 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci if (crtc->enabled) { 58462306a36Sopenharmony_ci drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); 58562306a36Sopenharmony_ci crtc_resp->mode_valid = 1; 58662306a36Sopenharmony_ci 58762306a36Sopenharmony_ci } else { 58862306a36Sopenharmony_ci crtc_resp->mode_valid = 0; 58962306a36Sopenharmony_ci } 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci if (!file_priv->aspect_ratio_allowed) 59262306a36Sopenharmony_ci crtc_resp->mode.flags &= ~DRM_MODE_FLAG_PIC_AR_MASK; 59362306a36Sopenharmony_ci drm_modeset_unlock(&crtc->mutex); 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci return 0; 59662306a36Sopenharmony_ci} 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_cistatic int __drm_mode_set_config_internal(struct drm_mode_set *set, 59962306a36Sopenharmony_ci struct drm_modeset_acquire_ctx *ctx) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci struct drm_crtc *crtc = set->crtc; 60262306a36Sopenharmony_ci struct drm_framebuffer *fb; 60362306a36Sopenharmony_ci struct drm_crtc *tmp; 60462306a36Sopenharmony_ci int ret; 60562306a36Sopenharmony_ci 60662306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(crtc->dev)); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci /* 60962306a36Sopenharmony_ci * NOTE: ->set_config can also disable other crtcs (if we steal all 61062306a36Sopenharmony_ci * connectors from it), hence we need to refcount the fbs across all 61162306a36Sopenharmony_ci * crtcs. Atomic modeset will have saner semantics ... 61262306a36Sopenharmony_ci */ 61362306a36Sopenharmony_ci drm_for_each_crtc(tmp, crtc->dev) { 61462306a36Sopenharmony_ci struct drm_plane *plane = tmp->primary; 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci plane->old_fb = plane->fb; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci 61962306a36Sopenharmony_ci fb = set->fb; 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci ret = crtc->funcs->set_config(set, ctx); 62262306a36Sopenharmony_ci if (ret == 0) { 62362306a36Sopenharmony_ci struct drm_plane *plane = crtc->primary; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci plane->crtc = fb ? crtc : NULL; 62662306a36Sopenharmony_ci plane->fb = fb; 62762306a36Sopenharmony_ci } 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci drm_for_each_crtc(tmp, crtc->dev) { 63062306a36Sopenharmony_ci struct drm_plane *plane = tmp->primary; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (plane->fb) 63362306a36Sopenharmony_ci drm_framebuffer_get(plane->fb); 63462306a36Sopenharmony_ci if (plane->old_fb) 63562306a36Sopenharmony_ci drm_framebuffer_put(plane->old_fb); 63662306a36Sopenharmony_ci plane->old_fb = NULL; 63762306a36Sopenharmony_ci } 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci return ret; 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci/** 64362306a36Sopenharmony_ci * drm_mode_set_config_internal - helper to call &drm_mode_config_funcs.set_config 64462306a36Sopenharmony_ci * @set: modeset config to set 64562306a36Sopenharmony_ci * 64662306a36Sopenharmony_ci * This is a little helper to wrap internal calls to the 64762306a36Sopenharmony_ci * &drm_mode_config_funcs.set_config driver interface. The only thing it adds is 64862306a36Sopenharmony_ci * correct refcounting dance. 64962306a36Sopenharmony_ci * 65062306a36Sopenharmony_ci * This should only be used by non-atomic legacy drivers. 65162306a36Sopenharmony_ci * 65262306a36Sopenharmony_ci * Returns: 65362306a36Sopenharmony_ci * Zero on success, negative errno on failure. 65462306a36Sopenharmony_ci */ 65562306a36Sopenharmony_ciint drm_mode_set_config_internal(struct drm_mode_set *set) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci WARN_ON(drm_drv_uses_atomic_modeset(set->crtc->dev)); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci return __drm_mode_set_config_internal(set, NULL); 66062306a36Sopenharmony_ci} 66162306a36Sopenharmony_ciEXPORT_SYMBOL(drm_mode_set_config_internal); 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci/** 66462306a36Sopenharmony_ci * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the 66562306a36Sopenharmony_ci * CRTC viewport 66662306a36Sopenharmony_ci * @crtc: CRTC that framebuffer will be displayed on 66762306a36Sopenharmony_ci * @x: x panning 66862306a36Sopenharmony_ci * @y: y panning 66962306a36Sopenharmony_ci * @mode: mode that framebuffer will be displayed under 67062306a36Sopenharmony_ci * @fb: framebuffer to check size of 67162306a36Sopenharmony_ci */ 67262306a36Sopenharmony_ciint drm_crtc_check_viewport(const struct drm_crtc *crtc, 67362306a36Sopenharmony_ci int x, int y, 67462306a36Sopenharmony_ci const struct drm_display_mode *mode, 67562306a36Sopenharmony_ci const struct drm_framebuffer *fb) 67662306a36Sopenharmony_ci 67762306a36Sopenharmony_ci{ 67862306a36Sopenharmony_ci int hdisplay, vdisplay; 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_ci drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay); 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (crtc->state && 68362306a36Sopenharmony_ci drm_rotation_90_or_270(crtc->primary->state->rotation)) 68462306a36Sopenharmony_ci swap(hdisplay, vdisplay); 68562306a36Sopenharmony_ci 68662306a36Sopenharmony_ci return drm_framebuffer_check_src_coords(x << 16, y << 16, 68762306a36Sopenharmony_ci hdisplay << 16, vdisplay << 16, 68862306a36Sopenharmony_ci fb); 68962306a36Sopenharmony_ci} 69062306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_check_viewport); 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci/** 69362306a36Sopenharmony_ci * drm_mode_setcrtc - set CRTC configuration 69462306a36Sopenharmony_ci * @dev: drm device for the ioctl 69562306a36Sopenharmony_ci * @data: data pointer for the ioctl 69662306a36Sopenharmony_ci * @file_priv: drm file for the ioctl call 69762306a36Sopenharmony_ci * 69862306a36Sopenharmony_ci * Build a new CRTC configuration based on user request. 69962306a36Sopenharmony_ci * 70062306a36Sopenharmony_ci * Called by the user via ioctl. 70162306a36Sopenharmony_ci * 70262306a36Sopenharmony_ci * Returns: 70362306a36Sopenharmony_ci * Zero on success, negative errno on failure. 70462306a36Sopenharmony_ci */ 70562306a36Sopenharmony_ciint drm_mode_setcrtc(struct drm_device *dev, void *data, 70662306a36Sopenharmony_ci struct drm_file *file_priv) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct drm_mode_config *config = &dev->mode_config; 70962306a36Sopenharmony_ci struct drm_mode_crtc *crtc_req = data; 71062306a36Sopenharmony_ci struct drm_crtc *crtc; 71162306a36Sopenharmony_ci struct drm_plane *plane; 71262306a36Sopenharmony_ci struct drm_connector **connector_set = NULL, *connector; 71362306a36Sopenharmony_ci struct drm_framebuffer *fb = NULL; 71462306a36Sopenharmony_ci struct drm_display_mode *mode = NULL; 71562306a36Sopenharmony_ci struct drm_mode_set set; 71662306a36Sopenharmony_ci uint32_t __user *set_connectors_ptr; 71762306a36Sopenharmony_ci struct drm_modeset_acquire_ctx ctx; 71862306a36Sopenharmony_ci int ret, i, num_connectors = 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci if (!drm_core_check_feature(dev, DRIVER_MODESET)) 72162306a36Sopenharmony_ci return -EOPNOTSUPP; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* 72462306a36Sopenharmony_ci * Universal plane src offsets are only 16.16, prevent havoc for 72562306a36Sopenharmony_ci * drivers using universal plane code internally. 72662306a36Sopenharmony_ci */ 72762306a36Sopenharmony_ci if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000) 72862306a36Sopenharmony_ci return -ERANGE; 72962306a36Sopenharmony_ci 73062306a36Sopenharmony_ci crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id); 73162306a36Sopenharmony_ci if (!crtc) { 73262306a36Sopenharmony_ci DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 73362306a36Sopenharmony_ci return -ENOENT; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci plane = crtc->primary; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci /* allow disabling with the primary plane leased */ 74062306a36Sopenharmony_ci if (crtc_req->mode_valid && !drm_lease_held(file_priv, plane->base.id)) 74162306a36Sopenharmony_ci return -EACCES; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_BEGIN(dev, ctx, 74462306a36Sopenharmony_ci DRM_MODESET_ACQUIRE_INTERRUPTIBLE, ret); 74562306a36Sopenharmony_ci 74662306a36Sopenharmony_ci if (crtc_req->mode_valid) { 74762306a36Sopenharmony_ci /* If we have a mode we need a framebuffer. */ 74862306a36Sopenharmony_ci /* If we pass -1, set the mode with the currently bound fb */ 74962306a36Sopenharmony_ci if (crtc_req->fb_id == -1) { 75062306a36Sopenharmony_ci struct drm_framebuffer *old_fb; 75162306a36Sopenharmony_ci 75262306a36Sopenharmony_ci if (plane->state) 75362306a36Sopenharmony_ci old_fb = plane->state->fb; 75462306a36Sopenharmony_ci else 75562306a36Sopenharmony_ci old_fb = plane->fb; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci if (!old_fb) { 75862306a36Sopenharmony_ci DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 75962306a36Sopenharmony_ci ret = -EINVAL; 76062306a36Sopenharmony_ci goto out; 76162306a36Sopenharmony_ci } 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci fb = old_fb; 76462306a36Sopenharmony_ci /* Make refcounting symmetric with the lookup path. */ 76562306a36Sopenharmony_ci drm_framebuffer_get(fb); 76662306a36Sopenharmony_ci } else { 76762306a36Sopenharmony_ci fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id); 76862306a36Sopenharmony_ci if (!fb) { 76962306a36Sopenharmony_ci DRM_DEBUG_KMS("Unknown FB ID%d\n", 77062306a36Sopenharmony_ci crtc_req->fb_id); 77162306a36Sopenharmony_ci ret = -ENOENT; 77262306a36Sopenharmony_ci goto out; 77362306a36Sopenharmony_ci } 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci mode = drm_mode_create(dev); 77762306a36Sopenharmony_ci if (!mode) { 77862306a36Sopenharmony_ci ret = -ENOMEM; 77962306a36Sopenharmony_ci goto out; 78062306a36Sopenharmony_ci } 78162306a36Sopenharmony_ci if (!file_priv->aspect_ratio_allowed && 78262306a36Sopenharmony_ci (crtc_req->mode.flags & DRM_MODE_FLAG_PIC_AR_MASK) != DRM_MODE_FLAG_PIC_AR_NONE) { 78362306a36Sopenharmony_ci DRM_DEBUG_KMS("Unexpected aspect-ratio flag bits\n"); 78462306a36Sopenharmony_ci ret = -EINVAL; 78562306a36Sopenharmony_ci goto out; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_ci ret = drm_mode_convert_umode(dev, mode, &crtc_req->mode); 79062306a36Sopenharmony_ci if (ret) { 79162306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid mode (ret=%d, status=%s)\n", 79262306a36Sopenharmony_ci ret, drm_get_mode_status_name(mode->status)); 79362306a36Sopenharmony_ci drm_mode_debug_printmodeline(mode); 79462306a36Sopenharmony_ci goto out; 79562306a36Sopenharmony_ci } 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci /* 79862306a36Sopenharmony_ci * Check whether the primary plane supports the fb pixel format. 79962306a36Sopenharmony_ci * Drivers not implementing the universal planes API use a 80062306a36Sopenharmony_ci * default formats list provided by the DRM core which doesn't 80162306a36Sopenharmony_ci * match real hardware capabilities. Skip the check in that 80262306a36Sopenharmony_ci * case. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci if (!plane->format_default) { 80562306a36Sopenharmony_ci ret = drm_plane_check_pixel_format(plane, 80662306a36Sopenharmony_ci fb->format->format, 80762306a36Sopenharmony_ci fb->modifier); 80862306a36Sopenharmony_ci if (ret) { 80962306a36Sopenharmony_ci DRM_DEBUG_KMS("Invalid pixel format %p4cc, modifier 0x%llx\n", 81062306a36Sopenharmony_ci &fb->format->format, 81162306a36Sopenharmony_ci fb->modifier); 81262306a36Sopenharmony_ci goto out; 81362306a36Sopenharmony_ci } 81462306a36Sopenharmony_ci } 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, 81762306a36Sopenharmony_ci mode, fb); 81862306a36Sopenharmony_ci if (ret) 81962306a36Sopenharmony_ci goto out; 82062306a36Sopenharmony_ci 82162306a36Sopenharmony_ci } 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci if (crtc_req->count_connectors == 0 && mode) { 82462306a36Sopenharmony_ci DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 82562306a36Sopenharmony_ci ret = -EINVAL; 82662306a36Sopenharmony_ci goto out; 82762306a36Sopenharmony_ci } 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 83062306a36Sopenharmony_ci DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 83162306a36Sopenharmony_ci crtc_req->count_connectors); 83262306a36Sopenharmony_ci ret = -EINVAL; 83362306a36Sopenharmony_ci goto out; 83462306a36Sopenharmony_ci } 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (crtc_req->count_connectors > 0) { 83762306a36Sopenharmony_ci u32 out_id; 83862306a36Sopenharmony_ci 83962306a36Sopenharmony_ci /* Avoid unbounded kernel memory allocation */ 84062306a36Sopenharmony_ci if (crtc_req->count_connectors > config->num_connector) { 84162306a36Sopenharmony_ci ret = -EINVAL; 84262306a36Sopenharmony_ci goto out; 84362306a36Sopenharmony_ci } 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci connector_set = kmalloc_array(crtc_req->count_connectors, 84662306a36Sopenharmony_ci sizeof(struct drm_connector *), 84762306a36Sopenharmony_ci GFP_KERNEL); 84862306a36Sopenharmony_ci if (!connector_set) { 84962306a36Sopenharmony_ci ret = -ENOMEM; 85062306a36Sopenharmony_ci goto out; 85162306a36Sopenharmony_ci } 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci for (i = 0; i < crtc_req->count_connectors; i++) { 85462306a36Sopenharmony_ci connector_set[i] = NULL; 85562306a36Sopenharmony_ci set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; 85662306a36Sopenharmony_ci if (get_user(out_id, &set_connectors_ptr[i])) { 85762306a36Sopenharmony_ci ret = -EFAULT; 85862306a36Sopenharmony_ci goto out; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci connector = drm_connector_lookup(dev, file_priv, out_id); 86262306a36Sopenharmony_ci if (!connector) { 86362306a36Sopenharmony_ci DRM_DEBUG_KMS("Connector id %d unknown\n", 86462306a36Sopenharmony_ci out_id); 86562306a36Sopenharmony_ci ret = -ENOENT; 86662306a36Sopenharmony_ci goto out; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 86962306a36Sopenharmony_ci connector->base.id, 87062306a36Sopenharmony_ci connector->name); 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci connector_set[i] = connector; 87362306a36Sopenharmony_ci num_connectors++; 87462306a36Sopenharmony_ci } 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci set.crtc = crtc; 87862306a36Sopenharmony_ci set.x = crtc_req->x; 87962306a36Sopenharmony_ci set.y = crtc_req->y; 88062306a36Sopenharmony_ci set.mode = mode; 88162306a36Sopenharmony_ci set.connectors = connector_set; 88262306a36Sopenharmony_ci set.num_connectors = num_connectors; 88362306a36Sopenharmony_ci set.fb = fb; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (drm_drv_uses_atomic_modeset(dev)) 88662306a36Sopenharmony_ci ret = crtc->funcs->set_config(&set, &ctx); 88762306a36Sopenharmony_ci else 88862306a36Sopenharmony_ci ret = __drm_mode_set_config_internal(&set, &ctx); 88962306a36Sopenharmony_ci 89062306a36Sopenharmony_ciout: 89162306a36Sopenharmony_ci if (fb) 89262306a36Sopenharmony_ci drm_framebuffer_put(fb); 89362306a36Sopenharmony_ci 89462306a36Sopenharmony_ci if (connector_set) { 89562306a36Sopenharmony_ci for (i = 0; i < num_connectors; i++) { 89662306a36Sopenharmony_ci if (connector_set[i]) 89762306a36Sopenharmony_ci drm_connector_put(connector_set[i]); 89862306a36Sopenharmony_ci } 89962306a36Sopenharmony_ci } 90062306a36Sopenharmony_ci kfree(connector_set); 90162306a36Sopenharmony_ci drm_mode_destroy(dev, mode); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci /* In case we need to retry... */ 90462306a36Sopenharmony_ci connector_set = NULL; 90562306a36Sopenharmony_ci fb = NULL; 90662306a36Sopenharmony_ci mode = NULL; 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci DRM_MODESET_LOCK_ALL_END(dev, ctx, ret); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci return ret; 91162306a36Sopenharmony_ci} 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ciint drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, 91462306a36Sopenharmony_ci struct drm_property *property, 91562306a36Sopenharmony_ci uint64_t value) 91662306a36Sopenharmony_ci{ 91762306a36Sopenharmony_ci int ret = -EINVAL; 91862306a36Sopenharmony_ci struct drm_crtc *crtc = obj_to_crtc(obj); 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci if (crtc->funcs->set_property) 92162306a36Sopenharmony_ci ret = crtc->funcs->set_property(crtc, property, value); 92262306a36Sopenharmony_ci if (!ret) 92362306a36Sopenharmony_ci drm_object_property_set_value(obj, property, value); 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci return ret; 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci/** 92962306a36Sopenharmony_ci * drm_crtc_create_scaling_filter_property - create a new scaling filter 93062306a36Sopenharmony_ci * property 93162306a36Sopenharmony_ci * 93262306a36Sopenharmony_ci * @crtc: drm CRTC 93362306a36Sopenharmony_ci * @supported_filters: bitmask of supported scaling filters, must include 93462306a36Sopenharmony_ci * BIT(DRM_SCALING_FILTER_DEFAULT). 93562306a36Sopenharmony_ci * 93662306a36Sopenharmony_ci * This function lets driver to enable the scaling filter property on a given 93762306a36Sopenharmony_ci * CRTC. 93862306a36Sopenharmony_ci * 93962306a36Sopenharmony_ci * RETURNS: 94062306a36Sopenharmony_ci * Zero for success or -errno 94162306a36Sopenharmony_ci */ 94262306a36Sopenharmony_ciint drm_crtc_create_scaling_filter_property(struct drm_crtc *crtc, 94362306a36Sopenharmony_ci unsigned int supported_filters) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct drm_property *prop = 94662306a36Sopenharmony_ci drm_create_scaling_filter_prop(crtc->dev, supported_filters); 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci if (IS_ERR(prop)) 94962306a36Sopenharmony_ci return PTR_ERR(prop); 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci drm_object_attach_property(&crtc->base, prop, 95262306a36Sopenharmony_ci DRM_SCALING_FILTER_DEFAULT); 95362306a36Sopenharmony_ci crtc->scaling_filter_property = prop; 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci return 0; 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ciEXPORT_SYMBOL(drm_crtc_create_scaling_filter_property); 958