xref: /openbmc/linux/drivers/gpu/drm/drm_crtc.c (revision 35f8cc3b)
1f453ba04SDave Airlie /*
2f453ba04SDave Airlie  * Copyright (c) 2006-2008 Intel Corporation
3f453ba04SDave Airlie  * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
4f453ba04SDave Airlie  * Copyright (c) 2008 Red Hat Inc.
5f453ba04SDave Airlie  *
6f453ba04SDave Airlie  * DRM core CRTC related functions
7f453ba04SDave Airlie  *
8f453ba04SDave Airlie  * Permission to use, copy, modify, distribute, and sell this software and its
9f453ba04SDave Airlie  * documentation for any purpose is hereby granted without fee, provided that
10f453ba04SDave Airlie  * the above copyright notice appear in all copies and that both that copyright
11f453ba04SDave Airlie  * notice and this permission notice appear in supporting documentation, and
12f453ba04SDave Airlie  * that the name of the copyright holders not be used in advertising or
13f453ba04SDave Airlie  * publicity pertaining to distribution of the software without specific,
14f453ba04SDave Airlie  * written prior permission.  The copyright holders make no representations
15f453ba04SDave Airlie  * about the suitability of this software for any purpose.  It is provided "as
16f453ba04SDave Airlie  * is" without express or implied warranty.
17f453ba04SDave Airlie  *
18f453ba04SDave Airlie  * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
19f453ba04SDave Airlie  * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
20f453ba04SDave Airlie  * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
21f453ba04SDave Airlie  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
22f453ba04SDave Airlie  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
23f453ba04SDave Airlie  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
24f453ba04SDave Airlie  * OF THIS SOFTWARE.
25f453ba04SDave Airlie  *
26f453ba04SDave Airlie  * Authors:
27f453ba04SDave Airlie  *      Keith Packard
28f453ba04SDave Airlie  *	Eric Anholt <eric@anholt.net>
29f453ba04SDave Airlie  *      Dave Airlie <airlied@linux.ie>
30f453ba04SDave Airlie  *      Jesse Barnes <jesse.barnes@intel.com>
31f453ba04SDave Airlie  */
326ba6d03eSVille Syrjälä #include <linux/ctype.h>
33f453ba04SDave Airlie #include <linux/list.h>
345a0e3ad6STejun Heo #include <linux/slab.h>
352d1a8a48SPaul Gortmaker #include <linux/export.h>
366d6003c4SGustavo Padovan #include <linux/dma-fence.h>
37760285e7SDavid Howells #include <drm/drmP.h>
38760285e7SDavid Howells #include <drm/drm_crtc.h>
39760285e7SDavid Howells #include <drm/drm_edid.h>
40760285e7SDavid Howells #include <drm/drm_fourcc.h>
4151fd371bSRob Clark #include <drm/drm_modeset_lock.h>
4288a48e29SRob Clark #include <drm/drm_atomic.h>
433b96a0b1SDaniel Vetter #include <drm/drm_auth.h>
449edbf1faSTomeu Vizoso #include <drm/drm_debugfs_crc.h>
45f453ba04SDave Airlie 
468bd441b2SDaniel Vetter #include "drm_crtc_internal.h"
4767d0ec4eSDaniel Vetter #include "drm_internal.h"
488bd441b2SDaniel Vetter 
496a0d9528SLukas Wunner /**
506a0d9528SLukas Wunner  * drm_crtc_force_disable - Forcibly turn off a CRTC
516a0d9528SLukas Wunner  * @crtc: CRTC to turn off
526a0d9528SLukas Wunner  *
536a0d9528SLukas Wunner  * Returns:
546a0d9528SLukas Wunner  * Zero on success, error code on failure.
556a0d9528SLukas Wunner  */
566a0d9528SLukas Wunner int drm_crtc_force_disable(struct drm_crtc *crtc)
576a0d9528SLukas Wunner {
586a0d9528SLukas Wunner 	struct drm_mode_set set = {
596a0d9528SLukas Wunner 		.crtc = crtc,
606a0d9528SLukas Wunner 	};
616a0d9528SLukas Wunner 
626a0d9528SLukas Wunner 	return drm_mode_set_config_internal(&set);
636a0d9528SLukas Wunner }
646a0d9528SLukas Wunner EXPORT_SYMBOL(drm_crtc_force_disable);
656a0d9528SLukas Wunner 
666a0d9528SLukas Wunner /**
676a0d9528SLukas Wunner  * drm_crtc_force_disable_all - Forcibly turn off all enabled CRTCs
686a0d9528SLukas Wunner  * @dev: DRM device whose CRTCs to turn off
696a0d9528SLukas Wunner  *
706a0d9528SLukas Wunner  * Drivers may want to call this on unload to ensure that all displays are
716a0d9528SLukas Wunner  * unlit and the GPU is in a consistent, low power state. Takes modeset locks.
726a0d9528SLukas Wunner  *
736a0d9528SLukas Wunner  * Returns:
746a0d9528SLukas Wunner  * Zero on success, error code on failure.
756a0d9528SLukas Wunner  */
766a0d9528SLukas Wunner int drm_crtc_force_disable_all(struct drm_device *dev)
776a0d9528SLukas Wunner {
786a0d9528SLukas Wunner 	struct drm_crtc *crtc;
796a0d9528SLukas Wunner 	int ret = 0;
806a0d9528SLukas Wunner 
816a0d9528SLukas Wunner 	drm_modeset_lock_all(dev);
826a0d9528SLukas Wunner 	drm_for_each_crtc(crtc, dev)
836a0d9528SLukas Wunner 		if (crtc->enabled) {
846a0d9528SLukas Wunner 			ret = drm_crtc_force_disable(crtc);
856a0d9528SLukas Wunner 			if (ret)
866a0d9528SLukas Wunner 				goto out;
876a0d9528SLukas Wunner 		}
886a0d9528SLukas Wunner out:
896a0d9528SLukas Wunner 	drm_modeset_unlock_all(dev);
906a0d9528SLukas Wunner 	return ret;
916a0d9528SLukas Wunner }
926a0d9528SLukas Wunner EXPORT_SYMBOL(drm_crtc_force_disable_all);
936a0d9528SLukas Wunner 
94fa3ab4c2SVille Syrjälä static unsigned int drm_num_crtcs(struct drm_device *dev)
95fa3ab4c2SVille Syrjälä {
96fa3ab4c2SVille Syrjälä 	unsigned int num = 0;
97fa3ab4c2SVille Syrjälä 	struct drm_crtc *tmp;
98fa3ab4c2SVille Syrjälä 
99fa3ab4c2SVille Syrjälä 	drm_for_each_crtc(tmp, dev) {
100fa3ab4c2SVille Syrjälä 		num++;
101fa3ab4c2SVille Syrjälä 	}
102fa3ab4c2SVille Syrjälä 
103fa3ab4c2SVille Syrjälä 	return num;
104fa3ab4c2SVille Syrjälä }
105fa3ab4c2SVille Syrjälä 
10628575f16SDaniel Vetter int drm_crtc_register_all(struct drm_device *dev)
10779190ea2SBenjamin Gaignard {
10879190ea2SBenjamin Gaignard 	struct drm_crtc *crtc;
10979190ea2SBenjamin Gaignard 	int ret = 0;
11079190ea2SBenjamin Gaignard 
11179190ea2SBenjamin Gaignard 	drm_for_each_crtc(crtc, dev) {
1129edbf1faSTomeu Vizoso 		if (drm_debugfs_crtc_add(crtc))
1139edbf1faSTomeu Vizoso 			DRM_ERROR("Failed to initialize debugfs entry for CRTC '%s'.\n",
1149edbf1faSTomeu Vizoso 				  crtc->name);
1159edbf1faSTomeu Vizoso 
11679190ea2SBenjamin Gaignard 		if (crtc->funcs->late_register)
11779190ea2SBenjamin Gaignard 			ret = crtc->funcs->late_register(crtc);
11879190ea2SBenjamin Gaignard 		if (ret)
11979190ea2SBenjamin Gaignard 			return ret;
12079190ea2SBenjamin Gaignard 	}
12179190ea2SBenjamin Gaignard 
12279190ea2SBenjamin Gaignard 	return 0;
12379190ea2SBenjamin Gaignard }
12479190ea2SBenjamin Gaignard 
12528575f16SDaniel Vetter void drm_crtc_unregister_all(struct drm_device *dev)
12679190ea2SBenjamin Gaignard {
12779190ea2SBenjamin Gaignard 	struct drm_crtc *crtc;
12879190ea2SBenjamin Gaignard 
12979190ea2SBenjamin Gaignard 	drm_for_each_crtc(crtc, dev) {
13079190ea2SBenjamin Gaignard 		if (crtc->funcs->early_unregister)
13179190ea2SBenjamin Gaignard 			crtc->funcs->early_unregister(crtc);
1329edbf1faSTomeu Vizoso 		drm_debugfs_crtc_remove(crtc);
13379190ea2SBenjamin Gaignard 	}
13479190ea2SBenjamin Gaignard }
13579190ea2SBenjamin Gaignard 
1369edbf1faSTomeu Vizoso static int drm_crtc_crc_init(struct drm_crtc *crtc)
1379edbf1faSTomeu Vizoso {
1389edbf1faSTomeu Vizoso #ifdef CONFIG_DEBUG_FS
1399edbf1faSTomeu Vizoso 	spin_lock_init(&crtc->crc.lock);
1409edbf1faSTomeu Vizoso 	init_waitqueue_head(&crtc->crc.wq);
1419edbf1faSTomeu Vizoso 	crtc->crc.source = kstrdup("auto", GFP_KERNEL);
1429edbf1faSTomeu Vizoso 	if (!crtc->crc.source)
1439edbf1faSTomeu Vizoso 		return -ENOMEM;
1449edbf1faSTomeu Vizoso #endif
1459edbf1faSTomeu Vizoso 	return 0;
1469edbf1faSTomeu Vizoso }
1479edbf1faSTomeu Vizoso 
1489edbf1faSTomeu Vizoso static void drm_crtc_crc_fini(struct drm_crtc *crtc)
1499edbf1faSTomeu Vizoso {
1509edbf1faSTomeu Vizoso #ifdef CONFIG_DEBUG_FS
1519edbf1faSTomeu Vizoso 	kfree(crtc->crc.source);
1529edbf1faSTomeu Vizoso #endif
1539edbf1faSTomeu Vizoso }
1549edbf1faSTomeu Vizoso 
15535f8cc3bSGustavo Padovan static const struct dma_fence_ops drm_crtc_fence_ops;
15635f8cc3bSGustavo Padovan 
1576d6003c4SGustavo Padovan static struct drm_crtc *fence_to_crtc(struct dma_fence *fence)
1586d6003c4SGustavo Padovan {
1596d6003c4SGustavo Padovan 	BUG_ON(fence->ops != &drm_crtc_fence_ops);
1606d6003c4SGustavo Padovan 	return container_of(fence->lock, struct drm_crtc, fence_lock);
1616d6003c4SGustavo Padovan }
1626d6003c4SGustavo Padovan 
1636d6003c4SGustavo Padovan static const char *drm_crtc_fence_get_driver_name(struct dma_fence *fence)
1646d6003c4SGustavo Padovan {
1656d6003c4SGustavo Padovan 	struct drm_crtc *crtc = fence_to_crtc(fence);
1666d6003c4SGustavo Padovan 
1676d6003c4SGustavo Padovan 	return crtc->dev->driver->name;
1686d6003c4SGustavo Padovan }
1696d6003c4SGustavo Padovan 
1706d6003c4SGustavo Padovan static const char *drm_crtc_fence_get_timeline_name(struct dma_fence *fence)
1716d6003c4SGustavo Padovan {
1726d6003c4SGustavo Padovan 	struct drm_crtc *crtc = fence_to_crtc(fence);
1736d6003c4SGustavo Padovan 
1746d6003c4SGustavo Padovan 	return crtc->timeline_name;
1756d6003c4SGustavo Padovan }
1766d6003c4SGustavo Padovan 
1776d6003c4SGustavo Padovan static bool drm_crtc_fence_enable_signaling(struct dma_fence *fence)
1786d6003c4SGustavo Padovan {
1796d6003c4SGustavo Padovan 	return true;
1806d6003c4SGustavo Padovan }
1816d6003c4SGustavo Padovan 
18235f8cc3bSGustavo Padovan static const struct dma_fence_ops drm_crtc_fence_ops = {
1836d6003c4SGustavo Padovan 	.get_driver_name = drm_crtc_fence_get_driver_name,
1846d6003c4SGustavo Padovan 	.get_timeline_name = drm_crtc_fence_get_timeline_name,
1856d6003c4SGustavo Padovan 	.enable_signaling = drm_crtc_fence_enable_signaling,
1866d6003c4SGustavo Padovan 	.wait = dma_fence_default_wait,
1876d6003c4SGustavo Padovan };
1886d6003c4SGustavo Padovan 
18935f8cc3bSGustavo Padovan struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
19035f8cc3bSGustavo Padovan {
19135f8cc3bSGustavo Padovan 	struct dma_fence *fence;
19235f8cc3bSGustavo Padovan 
19335f8cc3bSGustavo Padovan 	fence = kzalloc(sizeof(*fence), GFP_KERNEL);
19435f8cc3bSGustavo Padovan 	if (!fence)
19535f8cc3bSGustavo Padovan 		return NULL;
19635f8cc3bSGustavo Padovan 
19735f8cc3bSGustavo Padovan 	dma_fence_init(fence, &drm_crtc_fence_ops, &crtc->fence_lock,
19835f8cc3bSGustavo Padovan 		       crtc->fence_context, ++crtc->fence_seqno);
19935f8cc3bSGustavo Padovan 
20035f8cc3bSGustavo Padovan 	return fence;
20135f8cc3bSGustavo Padovan }
20235f8cc3bSGustavo Padovan 
203f453ba04SDave Airlie /**
204e13161afSMatt Roper  * drm_crtc_init_with_planes - Initialise a new CRTC object with
205e13161afSMatt Roper  *    specified primary and cursor planes.
206f453ba04SDave Airlie  * @dev: DRM device
207f453ba04SDave Airlie  * @crtc: CRTC object to init
208e13161afSMatt Roper  * @primary: Primary plane for CRTC
209e13161afSMatt Roper  * @cursor: Cursor plane for CRTC
210f453ba04SDave Airlie  * @funcs: callbacks for the new CRTC
211f9882876SVille Syrjälä  * @name: printf style format string for the CRTC name, or NULL for default name
212f453ba04SDave Airlie  *
213532b3671SDaniel Vetter  * Inits a new object created as base part of a driver crtc object. Drivers
214532b3671SDaniel Vetter  * should use this function instead of drm_crtc_init(), which is only provided
215532b3671SDaniel Vetter  * for backwards compatibility with drivers which do not yet support universal
216532b3671SDaniel Vetter  * planes). For really simple hardware which has only 1 plane look at
217532b3671SDaniel Vetter  * drm_simple_display_pipe_init() instead.
2186bfc56aaSVille Syrjälä  *
219c8e32cc1SDaniel Vetter  * Returns:
2206bfc56aaSVille Syrjälä  * Zero on success, error code on failure.
221f453ba04SDave Airlie  */
222e13161afSMatt Roper int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
223e13161afSMatt Roper 			      struct drm_plane *primary,
224fc1d3e44SMatt Roper 			      struct drm_plane *cursor,
225f9882876SVille Syrjälä 			      const struct drm_crtc_funcs *funcs,
226f9882876SVille Syrjälä 			      const char *name, ...)
227f453ba04SDave Airlie {
22851fd371bSRob Clark 	struct drm_mode_config *config = &dev->mode_config;
2296bfc56aaSVille Syrjälä 	int ret;
2306bfc56aaSVille Syrjälä 
231522cf91fSBenjamin Gaignard 	WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
232522cf91fSBenjamin Gaignard 	WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
233522cf91fSBenjamin Gaignard 
234f453ba04SDave Airlie 	crtc->dev = dev;
235f453ba04SDave Airlie 	crtc->funcs = funcs;
236f453ba04SDave Airlie 
2373b24f7d6SDaniel Vetter 	INIT_LIST_HEAD(&crtc->commit_list);
2383b24f7d6SDaniel Vetter 	spin_lock_init(&crtc->commit_lock);
2393b24f7d6SDaniel Vetter 
24051fd371bSRob Clark 	drm_modeset_lock_init(&crtc->mutex);
2416bfc56aaSVille Syrjälä 	ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
2426bfc56aaSVille Syrjälä 	if (ret)
243baf698b0SDaniel Vetter 		return ret;
244f453ba04SDave Airlie 
245fa3ab4c2SVille Syrjälä 	if (name) {
246fa3ab4c2SVille Syrjälä 		va_list ap;
247fa3ab4c2SVille Syrjälä 
248fa3ab4c2SVille Syrjälä 		va_start(ap, name);
249fa3ab4c2SVille Syrjälä 		crtc->name = kvasprintf(GFP_KERNEL, name, ap);
250fa3ab4c2SVille Syrjälä 		va_end(ap);
251fa3ab4c2SVille Syrjälä 	} else {
252fa3ab4c2SVille Syrjälä 		crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
253fa3ab4c2SVille Syrjälä 				       drm_num_crtcs(dev));
254fa3ab4c2SVille Syrjälä 	}
255fa3ab4c2SVille Syrjälä 	if (!crtc->name) {
2567c8f6d25SDave Airlie 		drm_mode_object_unregister(dev, &crtc->base);
257fa3ab4c2SVille Syrjälä 		return -ENOMEM;
258fa3ab4c2SVille Syrjälä 	}
259fa3ab4c2SVille Syrjälä 
2606d6003c4SGustavo Padovan 	crtc->fence_context = dma_fence_context_alloc(1);
2616d6003c4SGustavo Padovan 	spin_lock_init(&crtc->fence_lock);
2626d6003c4SGustavo Padovan 	snprintf(crtc->timeline_name, sizeof(crtc->timeline_name),
2636d6003c4SGustavo Padovan 		 "CRTC:%d-%s", crtc->base.id, crtc->name);
2646d6003c4SGustavo Padovan 
265bffd9de0SPaulo Zanoni 	crtc->base.properties = &crtc->properties;
266bffd9de0SPaulo Zanoni 
26751fd371bSRob Clark 	list_add_tail(&crtc->head, &config->crtc_list);
268490d3d1bSChris Wilson 	crtc->index = config->num_crtc++;
2696bfc56aaSVille Syrjälä 
270e13161afSMatt Roper 	crtc->primary = primary;
271fc1d3e44SMatt Roper 	crtc->cursor = cursor;
2727abc7d47SRob Clark 	if (primary && !primary->possible_crtcs)
273e13161afSMatt Roper 		primary->possible_crtcs = 1 << drm_crtc_index(crtc);
2747abc7d47SRob Clark 	if (cursor && !cursor->possible_crtcs)
275fc1d3e44SMatt Roper 		cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
276e13161afSMatt Roper 
2779edbf1faSTomeu Vizoso 	ret = drm_crtc_crc_init(crtc);
2789edbf1faSTomeu Vizoso 	if (ret) {
2799edbf1faSTomeu Vizoso 		drm_mode_object_unregister(dev, &crtc->base);
2809edbf1faSTomeu Vizoso 		return ret;
2819edbf1faSTomeu Vizoso 	}
2829edbf1faSTomeu Vizoso 
283eab3bbefSDaniel Vetter 	if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
284eab3bbefSDaniel Vetter 		drm_object_attach_property(&crtc->base, config->prop_active, 0);
285955f3c33SDaniel Stone 		drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
286beaf5af4SGustavo Padovan 		drm_object_attach_property(&crtc->base,
287beaf5af4SGustavo Padovan 					   config->prop_out_fence_ptr, 0);
288eab3bbefSDaniel Vetter 	}
289eab3bbefSDaniel Vetter 
290baf698b0SDaniel Vetter 	return 0;
291f453ba04SDave Airlie }
292e13161afSMatt Roper EXPORT_SYMBOL(drm_crtc_init_with_planes);
293f453ba04SDave Airlie 
294f453ba04SDave Airlie /**
295ad6f5c34SVille Syrjälä  * drm_crtc_cleanup - Clean up the core crtc usage
296f453ba04SDave Airlie  * @crtc: CRTC to cleanup
297f453ba04SDave Airlie  *
298ad6f5c34SVille Syrjälä  * This function cleans up @crtc and removes it from the DRM mode setting
299ad6f5c34SVille Syrjälä  * core. Note that the function does *not* free the crtc structure itself,
300ad6f5c34SVille Syrjälä  * this is the responsibility of the caller.
301f453ba04SDave Airlie  */
302f453ba04SDave Airlie void drm_crtc_cleanup(struct drm_crtc *crtc)
303f453ba04SDave Airlie {
304f453ba04SDave Airlie 	struct drm_device *dev = crtc->dev;
305f453ba04SDave Airlie 
306490d3d1bSChris Wilson 	/* Note that the crtc_list is considered to be static; should we
307490d3d1bSChris Wilson 	 * remove the drm_crtc at runtime we would have to decrement all
308490d3d1bSChris Wilson 	 * the indices on the drm_crtc after us in the crtc_list.
309490d3d1bSChris Wilson 	 */
310490d3d1bSChris Wilson 
3119edbf1faSTomeu Vizoso 	drm_crtc_crc_fini(crtc);
3129edbf1faSTomeu Vizoso 
313f453ba04SDave Airlie 	kfree(crtc->gamma_store);
314f453ba04SDave Airlie 	crtc->gamma_store = NULL;
315f453ba04SDave Airlie 
31651fd371bSRob Clark 	drm_modeset_lock_fini(&crtc->mutex);
31751fd371bSRob Clark 
3187c8f6d25SDave Airlie 	drm_mode_object_unregister(dev, &crtc->base);
319f453ba04SDave Airlie 	list_del(&crtc->head);
320f453ba04SDave Airlie 	dev->mode_config.num_crtc--;
3213009c037SThierry Reding 
3223009c037SThierry Reding 	WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state);
3233009c037SThierry Reding 	if (crtc->state && crtc->funcs->atomic_destroy_state)
3243009c037SThierry Reding 		crtc->funcs->atomic_destroy_state(crtc, crtc->state);
325a18c0af1SThierry Reding 
326fa3ab4c2SVille Syrjälä 	kfree(crtc->name);
327fa3ab4c2SVille Syrjälä 
328a18c0af1SThierry Reding 	memset(crtc, 0, sizeof(*crtc));
329f453ba04SDave Airlie }
330f453ba04SDave Airlie EXPORT_SYMBOL(drm_crtc_cleanup);
331f453ba04SDave Airlie 
332f453ba04SDave Airlie /**
333f453ba04SDave Airlie  * drm_mode_getcrtc - get CRTC configuration
334065a50edSDaniel Vetter  * @dev: drm device for the ioctl
335065a50edSDaniel Vetter  * @data: data pointer for the ioctl
336065a50edSDaniel Vetter  * @file_priv: drm file for the ioctl call
337f453ba04SDave Airlie  *
338f453ba04SDave Airlie  * Construct a CRTC configuration structure to return to the user.
339f453ba04SDave Airlie  *
340f453ba04SDave Airlie  * Called by the user via ioctl.
341f453ba04SDave Airlie  *
342c8e32cc1SDaniel Vetter  * Returns:
3431a498633SDaniel Vetter  * Zero on success, negative errno on failure.
344f453ba04SDave Airlie  */
345f453ba04SDave Airlie int drm_mode_getcrtc(struct drm_device *dev,
346f453ba04SDave Airlie 		     void *data, struct drm_file *file_priv)
347f453ba04SDave Airlie {
348f453ba04SDave Airlie 	struct drm_mode_crtc *crtc_resp = data;
349f453ba04SDave Airlie 	struct drm_crtc *crtc;
350f453ba04SDave Airlie 
351fb3b06c8SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
352fb3b06c8SDave Airlie 		return -EINVAL;
353fb3b06c8SDave Airlie 
354a2b34e22SRob Clark 	crtc = drm_crtc_find(dev, crtc_resp->crtc_id);
355fcf93f69SDaniel Vetter 	if (!crtc)
356fcf93f69SDaniel Vetter 		return -ENOENT;
357f453ba04SDave Airlie 
358fcf93f69SDaniel Vetter 	drm_modeset_lock_crtc(crtc, crtc->primary);
359f453ba04SDave Airlie 	crtc_resp->gamma_size = crtc->gamma_size;
360f4510a27SMatt Roper 	if (crtc->primary->fb)
361f4510a27SMatt Roper 		crtc_resp->fb_id = crtc->primary->fb->base.id;
362f453ba04SDave Airlie 	else
363f453ba04SDave Airlie 		crtc_resp->fb_id = 0;
364f453ba04SDave Airlie 
36531c946e8SDaniel Vetter 	if (crtc->state) {
36631c946e8SDaniel Vetter 		crtc_resp->x = crtc->primary->state->src_x >> 16;
36731c946e8SDaniel Vetter 		crtc_resp->y = crtc->primary->state->src_y >> 16;
36831c946e8SDaniel Vetter 		if (crtc->state->enable) {
369934a8a89SDaniel Stone 			drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
37031c946e8SDaniel Vetter 			crtc_resp->mode_valid = 1;
371f453ba04SDave Airlie 
37231c946e8SDaniel Vetter 		} else {
37331c946e8SDaniel Vetter 			crtc_resp->mode_valid = 0;
37431c946e8SDaniel Vetter 		}
37531c946e8SDaniel Vetter 	} else {
37631c946e8SDaniel Vetter 		crtc_resp->x = crtc->x;
37731c946e8SDaniel Vetter 		crtc_resp->y = crtc->y;
37831c946e8SDaniel Vetter 		if (crtc->enabled) {
379934a8a89SDaniel Stone 			drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
380f453ba04SDave Airlie 			crtc_resp->mode_valid = 1;
381f453ba04SDave Airlie 
382f453ba04SDave Airlie 		} else {
383f453ba04SDave Airlie 			crtc_resp->mode_valid = 0;
384f453ba04SDave Airlie 		}
38531c946e8SDaniel Vetter 	}
386fcf93f69SDaniel Vetter 	drm_modeset_unlock_crtc(crtc);
387f453ba04SDave Airlie 
388baf698b0SDaniel Vetter 	return 0;
389f453ba04SDave Airlie }
390f453ba04SDave Airlie 
391f453ba04SDave Airlie /**
3922d13b679SDaniel Vetter  * drm_mode_set_config_internal - helper to call ->set_config
3932d13b679SDaniel Vetter  * @set: modeset config to set
3942d13b679SDaniel Vetter  *
3952d13b679SDaniel Vetter  * This is a little helper to wrap internal calls to the ->set_config driver
3962d13b679SDaniel Vetter  * interface. The only thing it adds is correct refcounting dance.
397c8e32cc1SDaniel Vetter  *
398c8e32cc1SDaniel Vetter  * Returns:
3991a498633SDaniel Vetter  * Zero on success, negative errno on failure.
4002d13b679SDaniel Vetter  */
4012d13b679SDaniel Vetter int drm_mode_set_config_internal(struct drm_mode_set *set)
4022d13b679SDaniel Vetter {
4032d13b679SDaniel Vetter 	struct drm_crtc *crtc = set->crtc;
4045cef29aaSDaniel Vetter 	struct drm_framebuffer *fb;
4055cef29aaSDaniel Vetter 	struct drm_crtc *tmp;
406b0d12325SDaniel Vetter 	int ret;
4072d13b679SDaniel Vetter 
4085cef29aaSDaniel Vetter 	/*
4095cef29aaSDaniel Vetter 	 * NOTE: ->set_config can also disable other crtcs (if we steal all
4105cef29aaSDaniel Vetter 	 * connectors from it), hence we need to refcount the fbs across all
4115cef29aaSDaniel Vetter 	 * crtcs. Atomic modeset will have saner semantics ...
4125cef29aaSDaniel Vetter 	 */
413e4f62546SDaniel Vetter 	drm_for_each_crtc(tmp, crtc->dev)
4143d30a59bSDaniel Vetter 		tmp->primary->old_fb = tmp->primary->fb;
4155cef29aaSDaniel Vetter 
416b0d12325SDaniel Vetter 	fb = set->fb;
417b0d12325SDaniel Vetter 
418b0d12325SDaniel Vetter 	ret = crtc->funcs->set_config(set);
419b0d12325SDaniel Vetter 	if (ret == 0) {
420e13161afSMatt Roper 		crtc->primary->crtc = crtc;
4210fe27f06SDaniel Vetter 		crtc->primary->fb = fb;
4225cef29aaSDaniel Vetter 	}
423cc85e121SDaniel Vetter 
424e4f62546SDaniel Vetter 	drm_for_each_crtc(tmp, crtc->dev) {
425f4510a27SMatt Roper 		if (tmp->primary->fb)
426f4510a27SMatt Roper 			drm_framebuffer_reference(tmp->primary->fb);
4273d30a59bSDaniel Vetter 		if (tmp->primary->old_fb)
4283d30a59bSDaniel Vetter 			drm_framebuffer_unreference(tmp->primary->old_fb);
4293d30a59bSDaniel Vetter 		tmp->primary->old_fb = NULL;
430b0d12325SDaniel Vetter 	}
431b0d12325SDaniel Vetter 
432b0d12325SDaniel Vetter 	return ret;
4332d13b679SDaniel Vetter }
4342d13b679SDaniel Vetter EXPORT_SYMBOL(drm_mode_set_config_internal);
4352d13b679SDaniel Vetter 
436af93629dSMatt Roper /**
437ecb7e16bSGustavo Padovan  * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode
438ecb7e16bSGustavo Padovan  * @mode: mode to query
439ecb7e16bSGustavo Padovan  * @hdisplay: hdisplay value to fill in
440ecb7e16bSGustavo Padovan  * @vdisplay: vdisplay value to fill in
441ecb7e16bSGustavo Padovan  *
442ecb7e16bSGustavo Padovan  * The vdisplay value will be doubled if the specified mode is a stereo mode of
443ecb7e16bSGustavo Padovan  * the appropriate layout.
444ecb7e16bSGustavo Padovan  */
445ecb7e16bSGustavo Padovan void drm_crtc_get_hv_timing(const struct drm_display_mode *mode,
446ecb7e16bSGustavo Padovan 			    int *hdisplay, int *vdisplay)
447ecb7e16bSGustavo Padovan {
448ecb7e16bSGustavo Padovan 	struct drm_display_mode adjusted;
449ecb7e16bSGustavo Padovan 
450ecb7e16bSGustavo Padovan 	drm_mode_copy(&adjusted, mode);
451ecb7e16bSGustavo Padovan 	drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY);
452ecb7e16bSGustavo Padovan 	*hdisplay = adjusted.crtc_hdisplay;
453ecb7e16bSGustavo Padovan 	*vdisplay = adjusted.crtc_vdisplay;
454ecb7e16bSGustavo Padovan }
455ecb7e16bSGustavo Padovan EXPORT_SYMBOL(drm_crtc_get_hv_timing);
456ecb7e16bSGustavo Padovan 
457ecb7e16bSGustavo Padovan /**
458af93629dSMatt Roper  * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
459af93629dSMatt Roper  *     CRTC viewport
460af93629dSMatt Roper  * @crtc: CRTC that framebuffer will be displayed on
461af93629dSMatt Roper  * @x: x panning
462af93629dSMatt Roper  * @y: y panning
463af93629dSMatt Roper  * @mode: mode that framebuffer will be displayed under
464af93629dSMatt Roper  * @fb: framebuffer to check size of
465c11e9283SDamien Lespiau  */
466af93629dSMatt Roper int drm_crtc_check_viewport(const struct drm_crtc *crtc,
467c11e9283SDamien Lespiau 			    int x, int y,
468c11e9283SDamien Lespiau 			    const struct drm_display_mode *mode,
469c11e9283SDamien Lespiau 			    const struct drm_framebuffer *fb)
470c11e9283SDamien Lespiau 
471c11e9283SDamien Lespiau {
472c11e9283SDamien Lespiau 	int hdisplay, vdisplay;
473c11e9283SDamien Lespiau 
474ecb7e16bSGustavo Padovan 	drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay);
475a0c1bbb0SDamien Lespiau 
47633e0be63SVille Syrjälä 	if (crtc->state &&
477bd2ef25dSVille Syrjälä 	    drm_rotation_90_or_270(crtc->primary->state->rotation))
478c11e9283SDamien Lespiau 		swap(hdisplay, vdisplay);
479c11e9283SDamien Lespiau 
48043968d7bSDaniel Vetter 	return drm_framebuffer_check_src_coords(x << 16, y << 16,
48143968d7bSDaniel Vetter 						hdisplay << 16, vdisplay << 16,
48243968d7bSDaniel Vetter 						fb);
483c11e9283SDamien Lespiau }
484af93629dSMatt Roper EXPORT_SYMBOL(drm_crtc_check_viewport);
485c11e9283SDamien Lespiau 
4862d13b679SDaniel Vetter /**
487f453ba04SDave Airlie  * drm_mode_setcrtc - set CRTC configuration
488065a50edSDaniel Vetter  * @dev: drm device for the ioctl
489065a50edSDaniel Vetter  * @data: data pointer for the ioctl
490065a50edSDaniel Vetter  * @file_priv: drm file for the ioctl call
491f453ba04SDave Airlie  *
492f453ba04SDave Airlie  * Build a new CRTC configuration based on user request.
493f453ba04SDave Airlie  *
494f453ba04SDave Airlie  * Called by the user via ioctl.
495f453ba04SDave Airlie  *
496c8e32cc1SDaniel Vetter  * Returns:
4971a498633SDaniel Vetter  * Zero on success, negative errno on failure.
498f453ba04SDave Airlie  */
499f453ba04SDave Airlie int drm_mode_setcrtc(struct drm_device *dev, void *data,
500f453ba04SDave Airlie 		     struct drm_file *file_priv)
501f453ba04SDave Airlie {
502f453ba04SDave Airlie 	struct drm_mode_config *config = &dev->mode_config;
503f453ba04SDave Airlie 	struct drm_mode_crtc *crtc_req = data;
5046653cc8dSVille Syrjälä 	struct drm_crtc *crtc;
505f453ba04SDave Airlie 	struct drm_connector **connector_set = NULL, *connector;
506f453ba04SDave Airlie 	struct drm_framebuffer *fb = NULL;
507f453ba04SDave Airlie 	struct drm_display_mode *mode = NULL;
508f453ba04SDave Airlie 	struct drm_mode_set set;
509f453ba04SDave Airlie 	uint32_t __user *set_connectors_ptr;
5104a1b0714SLaurent Pinchart 	int ret;
511f453ba04SDave Airlie 	int i;
512f453ba04SDave Airlie 
513fb3b06c8SDave Airlie 	if (!drm_core_check_feature(dev, DRIVER_MODESET))
514fb3b06c8SDave Airlie 		return -EINVAL;
515fb3b06c8SDave Airlie 
51601447e9fSZhao Junwang 	/*
51701447e9fSZhao Junwang 	 * Universal plane src offsets are only 16.16, prevent havoc for
51801447e9fSZhao Junwang 	 * drivers using universal plane code internally.
51901447e9fSZhao Junwang 	 */
52001447e9fSZhao Junwang 	if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
5211d97e915SVille Syrjälä 		return -ERANGE;
5221d97e915SVille Syrjälä 
52384849903SDaniel Vetter 	drm_modeset_lock_all(dev);
524a2b34e22SRob Clark 	crtc = drm_crtc_find(dev, crtc_req->crtc_id);
525a2b34e22SRob Clark 	if (!crtc) {
52658367ed6SZhao Yakui 		DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
527f27657f2SVille Syrjälä 		ret = -ENOENT;
528f453ba04SDave Airlie 		goto out;
529f453ba04SDave Airlie 	}
530fa3ab4c2SVille Syrjälä 	DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
531f453ba04SDave Airlie 
532f453ba04SDave Airlie 	if (crtc_req->mode_valid) {
533f453ba04SDave Airlie 		/* If we have a mode we need a framebuffer. */
534f453ba04SDave Airlie 		/* If we pass -1, set the mode with the currently bound fb */
535f453ba04SDave Airlie 		if (crtc_req->fb_id == -1) {
536f4510a27SMatt Roper 			if (!crtc->primary->fb) {
5376653cc8dSVille Syrjälä 				DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
5386653cc8dSVille Syrjälä 				ret = -EINVAL;
5396653cc8dSVille Syrjälä 				goto out;
5406653cc8dSVille Syrjälä 			}
541f4510a27SMatt Roper 			fb = crtc->primary->fb;
542b0d12325SDaniel Vetter 			/* Make refcounting symmetric with the lookup path. */
543b0d12325SDaniel Vetter 			drm_framebuffer_reference(fb);
544f453ba04SDave Airlie 		} else {
545786b99edSDaniel Vetter 			fb = drm_framebuffer_lookup(dev, crtc_req->fb_id);
546786b99edSDaniel Vetter 			if (!fb) {
54758367ed6SZhao Yakui 				DRM_DEBUG_KMS("Unknown FB ID%d\n",
54858367ed6SZhao Yakui 						crtc_req->fb_id);
54937c4e705SVille Syrjälä 				ret = -ENOENT;
550f453ba04SDave Airlie 				goto out;
551f453ba04SDave Airlie 			}
552f453ba04SDave Airlie 		}
553f453ba04SDave Airlie 
554f453ba04SDave Airlie 		mode = drm_mode_create(dev);
555ee34ab5bSVille Syrjälä 		if (!mode) {
556ee34ab5bSVille Syrjälä 			ret = -ENOMEM;
557ee34ab5bSVille Syrjälä 			goto out;
558ee34ab5bSVille Syrjälä 		}
559ee34ab5bSVille Syrjälä 
560934a8a89SDaniel Stone 		ret = drm_mode_convert_umode(mode, &crtc_req->mode);
56190367bf6SVille Syrjälä 		if (ret) {
56290367bf6SVille Syrjälä 			DRM_DEBUG_KMS("Invalid mode\n");
56390367bf6SVille Syrjälä 			goto out;
56490367bf6SVille Syrjälä 		}
56590367bf6SVille Syrjälä 
5667eb5f302SLaurent Pinchart 		/*
5677eb5f302SLaurent Pinchart 		 * Check whether the primary plane supports the fb pixel format.
5687eb5f302SLaurent Pinchart 		 * Drivers not implementing the universal planes API use a
5697eb5f302SLaurent Pinchart 		 * default formats list provided by the DRM core which doesn't
5707eb5f302SLaurent Pinchart 		 * match real hardware capabilities. Skip the check in that
5717eb5f302SLaurent Pinchart 		 * case.
5727eb5f302SLaurent Pinchart 		 */
5737eb5f302SLaurent Pinchart 		if (!crtc->primary->format_default) {
5747eb5f302SLaurent Pinchart 			ret = drm_plane_check_pixel_format(crtc->primary,
5757eb5f302SLaurent Pinchart 							   fb->pixel_format);
5767eb5f302SLaurent Pinchart 			if (ret) {
577b3c11ac2SEric Engestrom 				struct drm_format_name_buf format_name;
578b3c11ac2SEric Engestrom 				DRM_DEBUG_KMS("Invalid pixel format %s\n",
579b3c11ac2SEric Engestrom 				              drm_get_format_name(fb->pixel_format,
580b3c11ac2SEric Engestrom 				                                  &format_name));
5817eb5f302SLaurent Pinchart 				goto out;
5827eb5f302SLaurent Pinchart 			}
5837eb5f302SLaurent Pinchart 		}
5847eb5f302SLaurent Pinchart 
585c11e9283SDamien Lespiau 		ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
586c11e9283SDamien Lespiau 					      mode, fb);
587c11e9283SDamien Lespiau 		if (ret)
5885f61bb42SVille Syrjälä 			goto out;
589c11e9283SDamien Lespiau 
590f453ba04SDave Airlie 	}
591f453ba04SDave Airlie 
592f453ba04SDave Airlie 	if (crtc_req->count_connectors == 0 && mode) {
59358367ed6SZhao Yakui 		DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
594f453ba04SDave Airlie 		ret = -EINVAL;
595f453ba04SDave Airlie 		goto out;
596f453ba04SDave Airlie 	}
597f453ba04SDave Airlie 
5987781de74SJakob Bornecrantz 	if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
59958367ed6SZhao Yakui 		DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
600f453ba04SDave Airlie 			  crtc_req->count_connectors);
601f453ba04SDave Airlie 		ret = -EINVAL;
602f453ba04SDave Airlie 		goto out;
603f453ba04SDave Airlie 	}
604f453ba04SDave Airlie 
605f453ba04SDave Airlie 	if (crtc_req->count_connectors > 0) {
606f453ba04SDave Airlie 		u32 out_id;
607f453ba04SDave Airlie 
608f453ba04SDave Airlie 		/* Avoid unbounded kernel memory allocation */
609f453ba04SDave Airlie 		if (crtc_req->count_connectors > config->num_connector) {
610f453ba04SDave Airlie 			ret = -EINVAL;
611f453ba04SDave Airlie 			goto out;
612f453ba04SDave Airlie 		}
613f453ba04SDave Airlie 
6142f6c5389SThierry Reding 		connector_set = kmalloc_array(crtc_req->count_connectors,
615f453ba04SDave Airlie 					      sizeof(struct drm_connector *),
616f453ba04SDave Airlie 					      GFP_KERNEL);
617f453ba04SDave Airlie 		if (!connector_set) {
618f453ba04SDave Airlie 			ret = -ENOMEM;
619f453ba04SDave Airlie 			goto out;
620f453ba04SDave Airlie 		}
621f453ba04SDave Airlie 
622f453ba04SDave Airlie 		for (i = 0; i < crtc_req->count_connectors; i++) {
623b164d31fSDave Airlie 			connector_set[i] = NULL;
62481f6c7f8SVille Syrjälä 			set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
625f453ba04SDave Airlie 			if (get_user(out_id, &set_connectors_ptr[i])) {
626f453ba04SDave Airlie 				ret = -EFAULT;
627f453ba04SDave Airlie 				goto out;
628f453ba04SDave Airlie 			}
629f453ba04SDave Airlie 
630b164d31fSDave Airlie 			connector = drm_connector_lookup(dev, out_id);
631a2b34e22SRob Clark 			if (!connector) {
63258367ed6SZhao Yakui 				DRM_DEBUG_KMS("Connector id %d unknown\n",
63358367ed6SZhao Yakui 						out_id);
634f27657f2SVille Syrjälä 				ret = -ENOENT;
635f453ba04SDave Airlie 				goto out;
636f453ba04SDave Airlie 			}
6379440106bSJerome Glisse 			DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
6389440106bSJerome Glisse 					connector->base.id,
63925933820SJani Nikula 					connector->name);
640f453ba04SDave Airlie 
641f453ba04SDave Airlie 			connector_set[i] = connector;
642f453ba04SDave Airlie 		}
643f453ba04SDave Airlie 	}
644f453ba04SDave Airlie 
645f453ba04SDave Airlie 	set.crtc = crtc;
646f453ba04SDave Airlie 	set.x = crtc_req->x;
647f453ba04SDave Airlie 	set.y = crtc_req->y;
648f453ba04SDave Airlie 	set.mode = mode;
649f453ba04SDave Airlie 	set.connectors = connector_set;
650f453ba04SDave Airlie 	set.num_connectors = crtc_req->count_connectors;
651f453ba04SDave Airlie 	set.fb = fb;
6522d13b679SDaniel Vetter 	ret = drm_mode_set_config_internal(&set);
653f453ba04SDave Airlie 
654f453ba04SDave Airlie out:
655b0d12325SDaniel Vetter 	if (fb)
656b0d12325SDaniel Vetter 		drm_framebuffer_unreference(fb);
657b0d12325SDaniel Vetter 
658b164d31fSDave Airlie 	if (connector_set) {
659b164d31fSDave Airlie 		for (i = 0; i < crtc_req->count_connectors; i++) {
660b164d31fSDave Airlie 			if (connector_set[i])
661b164d31fSDave Airlie 				drm_connector_unreference(connector_set[i]);
662b164d31fSDave Airlie 		}
663b164d31fSDave Airlie 	}
664f453ba04SDave Airlie 	kfree(connector_set);
665ee34ab5bSVille Syrjälä 	drm_mode_destroy(dev, mode);
66684849903SDaniel Vetter 	drm_modeset_unlock_all(dev);
667f453ba04SDave Airlie 	return ret;
668f453ba04SDave Airlie }
669f453ba04SDave Airlie 
670949619f3SDaniel Vetter int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
671bffd9de0SPaulo Zanoni 			       struct drm_property *property,
672bffd9de0SPaulo Zanoni 			       uint64_t value)
673bffd9de0SPaulo Zanoni {
674bffd9de0SPaulo Zanoni 	int ret = -EINVAL;
675bffd9de0SPaulo Zanoni 	struct drm_crtc *crtc = obj_to_crtc(obj);
676bffd9de0SPaulo Zanoni 
677bffd9de0SPaulo Zanoni 	if (crtc->funcs->set_property)
678bffd9de0SPaulo Zanoni 		ret = crtc->funcs->set_property(crtc, property, value);
679bffd9de0SPaulo Zanoni 	if (!ret)
680bffd9de0SPaulo Zanoni 		drm_object_property_set_value(obj, property, value);
681bffd9de0SPaulo Zanoni 
682bffd9de0SPaulo Zanoni 	return ret;
683bffd9de0SPaulo Zanoni }
684