15718399fSFrançois Tigeot /*
25718399fSFrançois Tigeot * Copyright (c) 2006-2008 Intel Corporation
35718399fSFrançois Tigeot * Copyright (c) 2007 Dave Airlie <airlied@linux.ie>
45718399fSFrançois Tigeot * Copyright (c) 2008 Red Hat Inc.
55718399fSFrançois Tigeot *
65718399fSFrançois Tigeot * DRM core CRTC related functions
75718399fSFrançois Tigeot *
85718399fSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its
95718399fSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that
105718399fSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright
115718399fSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and
125718399fSFrançois Tigeot * that the name of the copyright holders not be used in advertising or
135718399fSFrançois Tigeot * publicity pertaining to distribution of the software without specific,
145718399fSFrançois Tigeot * written prior permission. The copyright holders make no representations
155718399fSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as
165718399fSFrançois Tigeot * is" without express or implied warranty.
175718399fSFrançois Tigeot *
185718399fSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
195718399fSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
205718399fSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
215718399fSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
225718399fSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
235718399fSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
245718399fSFrançois Tigeot * OF THIS SOFTWARE.
255718399fSFrançois Tigeot *
265718399fSFrançois Tigeot * Authors:
275718399fSFrançois Tigeot * Keith Packard
285718399fSFrançois Tigeot * Eric Anholt <eric@anholt.net>
295718399fSFrançois Tigeot * Dave Airlie <airlied@linux.ie>
305718399fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com>
315718399fSFrançois Tigeot */
3206fede5aSFrançois Tigeot #include <linux/ctype.h>
33b5162e19SFrançois Tigeot #include <linux/list.h>
341e12ee3bSFrançois Tigeot #include <linux/slab.h>
35b5162e19SFrançois Tigeot #include <linux/export.h>
364be47400SFrançois Tigeot #include <linux/dma-fence.h>
3718e26a6dSFrançois Tigeot #include <drm/drmP.h>
3818e26a6dSFrançois Tigeot #include <drm/drm_crtc.h>
3918e26a6dSFrançois Tigeot #include <drm/drm_edid.h>
4083b4b9b9SFrançois Tigeot #include <drm/drm_fourcc.h>
41ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h>
422c9916cdSFrançois Tigeot #include <drm/drm_atomic.h>
431dedbd3bSFrançois Tigeot #include <drm/drm_auth.h>
444be47400SFrançois Tigeot #include <drm/drm_debugfs_crc.h>
45ba55f2f5SFrançois Tigeot
46ba55f2f5SFrançois Tigeot #include "drm_crtc_internal.h"
472c9916cdSFrançois Tigeot #include "drm_internal.h"
485718399fSFrançois Tigeot
49ba55f2f5SFrançois Tigeot /**
50a85cb24fSFrançois Tigeot * DOC: overview
51a85cb24fSFrançois Tigeot *
52a85cb24fSFrançois Tigeot * A CRTC represents the overall display pipeline. It receives pixel data from
53a85cb24fSFrançois Tigeot * &drm_plane and blends them together. The &drm_display_mode is also attached
54a85cb24fSFrançois Tigeot * to the CRTC, specifying display timings. On the output side the data is fed
55a85cb24fSFrançois Tigeot * to one or more &drm_encoder, which are then each connected to one
56a85cb24fSFrançois Tigeot * &drm_connector.
57a85cb24fSFrançois Tigeot *
58a85cb24fSFrançois Tigeot * To create a CRTC, a KMS drivers allocates and zeroes an instances of
59a85cb24fSFrançois Tigeot * &struct drm_crtc (possibly as part of a larger structure) and registers it
60a85cb24fSFrançois Tigeot * with a call to drm_crtc_init_with_planes().
61a85cb24fSFrançois Tigeot *
62a85cb24fSFrançois Tigeot * The CRTC is also the entry point for legacy modeset operations, see
63a85cb24fSFrançois Tigeot * &drm_crtc_funcs.set_config, legacy plane operations, see
64a85cb24fSFrançois Tigeot * &drm_crtc_funcs.page_flip and &drm_crtc_funcs.cursor_set2, and other legacy
65a85cb24fSFrançois Tigeot * operations like &drm_crtc_funcs.gamma_set. For atomic drivers all these
66a85cb24fSFrançois Tigeot * features are controlled through &drm_property and
67a85cb24fSFrançois Tigeot * &drm_mode_config_funcs.atomic_check and &drm_mode_config_funcs.atomic_check.
68a85cb24fSFrançois Tigeot */
69a85cb24fSFrançois Tigeot
70a85cb24fSFrançois Tigeot /**
71a85cb24fSFrançois Tigeot * drm_crtc_from_index - find the registered CRTC at an index
72a85cb24fSFrançois Tigeot * @dev: DRM device
73a85cb24fSFrançois Tigeot * @idx: index of registered CRTC to find for
74a85cb24fSFrançois Tigeot *
75a85cb24fSFrançois Tigeot * Given a CRTC index, return the registered CRTC from DRM device's
76a85cb24fSFrançois Tigeot * list of CRTCs with matching index. This is the inverse of drm_crtc_index().
77a85cb24fSFrançois Tigeot * It's useful in the vblank callbacks (like &drm_driver.enable_vblank or
78a85cb24fSFrançois Tigeot * &drm_driver.disable_vblank), since that still deals with indices instead
79a85cb24fSFrançois Tigeot * of pointers to &struct drm_crtc."
80a85cb24fSFrançois Tigeot */
drm_crtc_from_index(struct drm_device * dev,int idx)81a85cb24fSFrançois Tigeot struct drm_crtc *drm_crtc_from_index(struct drm_device *dev, int idx)
82a85cb24fSFrançois Tigeot {
83a85cb24fSFrançois Tigeot struct drm_crtc *crtc;
84a85cb24fSFrançois Tigeot
85a85cb24fSFrançois Tigeot drm_for_each_crtc(crtc, dev)
86a85cb24fSFrançois Tigeot if (idx == crtc->index)
87a85cb24fSFrançois Tigeot return crtc;
88a85cb24fSFrançois Tigeot
89a85cb24fSFrançois Tigeot return NULL;
90a85cb24fSFrançois Tigeot }
91a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_crtc_from_index);
92a85cb24fSFrançois Tigeot
93a85cb24fSFrançois Tigeot /**
941dedbd3bSFrançois Tigeot * drm_crtc_force_disable - Forcibly turn off a CRTC
951dedbd3bSFrançois Tigeot * @crtc: CRTC to turn off
965718399fSFrançois Tigeot *
97a85cb24fSFrançois Tigeot * Note: This should only be used by non-atomic legacy drivers.
98a85cb24fSFrançois Tigeot *
99ba55f2f5SFrançois Tigeot * Returns:
100352ff8bdSFrançois Tigeot * Zero on success, error code on failure.
1015718399fSFrançois Tigeot */
drm_crtc_force_disable(struct drm_crtc * crtc)1021dedbd3bSFrançois Tigeot int drm_crtc_force_disable(struct drm_crtc *crtc)
1035718399fSFrançois Tigeot {
1041dedbd3bSFrançois Tigeot struct drm_mode_set set = {
1051dedbd3bSFrançois Tigeot .crtc = crtc,
1061dedbd3bSFrançois Tigeot };
1079f0f5970SFrançois Tigeot
108a85cb24fSFrançois Tigeot WARN_ON(drm_drv_uses_atomic_modeset(crtc->dev));
109a85cb24fSFrançois Tigeot
1101dedbd3bSFrançois Tigeot return drm_mode_set_config_internal(&set);
1115718399fSFrançois Tigeot }
1121dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_crtc_force_disable);
1135718399fSFrançois Tigeot
1145718399fSFrançois Tigeot /**
1151dedbd3bSFrançois Tigeot * drm_crtc_force_disable_all - Forcibly turn off all enabled CRTCs
1161dedbd3bSFrançois Tigeot * @dev: DRM device whose CRTCs to turn off
1175718399fSFrançois Tigeot *
1181dedbd3bSFrançois Tigeot * Drivers may want to call this on unload to ensure that all displays are
1191dedbd3bSFrançois Tigeot * unlit and the GPU is in a consistent, low power state. Takes modeset locks.
1204dbb207bSFrançois Tigeot *
121a85cb24fSFrançois Tigeot * Note: This should only be used by non-atomic legacy drivers. For an atomic
122a85cb24fSFrançois Tigeot * version look at drm_atomic_helper_shutdown().
123a85cb24fSFrançois Tigeot *
124ba55f2f5SFrançois Tigeot * Returns:
1255718399fSFrançois Tigeot * Zero on success, error code on failure.
1265718399fSFrançois Tigeot */
drm_crtc_force_disable_all(struct drm_device * dev)1271dedbd3bSFrançois Tigeot int drm_crtc_force_disable_all(struct drm_device *dev)
1285718399fSFrançois Tigeot {
1291dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1301dedbd3bSFrançois Tigeot int ret = 0;
1315718399fSFrançois Tigeot
1321dedbd3bSFrançois Tigeot drm_modeset_lock_all(dev);
1331dedbd3bSFrançois Tigeot drm_for_each_crtc(crtc, dev)
1341dedbd3bSFrançois Tigeot if (crtc->enabled) {
1351dedbd3bSFrançois Tigeot ret = drm_crtc_force_disable(crtc);
1365718399fSFrançois Tigeot if (ret)
1374dbb207bSFrançois Tigeot goto out;
1381dedbd3bSFrançois Tigeot }
1398621f407SFrançois Tigeot out:
1401dedbd3bSFrançois Tigeot drm_modeset_unlock_all(dev);
141352ff8bdSFrançois Tigeot return ret;
1425718399fSFrançois Tigeot }
1431dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_crtc_force_disable_all);
1445718399fSFrançois Tigeot
drm_num_crtcs(struct drm_device * dev)145aee94f86SFrançois Tigeot static unsigned int drm_num_crtcs(struct drm_device *dev)
146aee94f86SFrançois Tigeot {
147aee94f86SFrançois Tigeot unsigned int num = 0;
148aee94f86SFrançois Tigeot struct drm_crtc *tmp;
149aee94f86SFrançois Tigeot
150aee94f86SFrançois Tigeot drm_for_each_crtc(tmp, dev) {
151aee94f86SFrançois Tigeot num++;
152aee94f86SFrançois Tigeot }
153aee94f86SFrançois Tigeot
154aee94f86SFrançois Tigeot return num;
155aee94f86SFrançois Tigeot }
156aee94f86SFrançois Tigeot
drm_crtc_register_all(struct drm_device * dev)1574be47400SFrançois Tigeot int drm_crtc_register_all(struct drm_device *dev)
1581dedbd3bSFrançois Tigeot {
1591dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1601dedbd3bSFrançois Tigeot int ret = 0;
1611dedbd3bSFrançois Tigeot
1621dedbd3bSFrançois Tigeot drm_for_each_crtc(crtc, dev) {
1634be47400SFrançois Tigeot if (drm_debugfs_crtc_add(crtc))
1644be47400SFrançois Tigeot DRM_ERROR("Failed to initialize debugfs entry for CRTC '%s'.\n",
1654be47400SFrançois Tigeot crtc->name);
1664be47400SFrançois Tigeot
1671dedbd3bSFrançois Tigeot if (crtc->funcs->late_register)
1681dedbd3bSFrançois Tigeot ret = crtc->funcs->late_register(crtc);
1691dedbd3bSFrançois Tigeot if (ret)
1701dedbd3bSFrançois Tigeot return ret;
1711dedbd3bSFrançois Tigeot }
1721dedbd3bSFrançois Tigeot
1731dedbd3bSFrançois Tigeot return 0;
1741dedbd3bSFrançois Tigeot }
1751dedbd3bSFrançois Tigeot
drm_crtc_unregister_all(struct drm_device * dev)1764be47400SFrançois Tigeot void drm_crtc_unregister_all(struct drm_device *dev)
1771dedbd3bSFrançois Tigeot {
1781dedbd3bSFrançois Tigeot struct drm_crtc *crtc;
1791dedbd3bSFrançois Tigeot
1801dedbd3bSFrançois Tigeot drm_for_each_crtc(crtc, dev) {
1811dedbd3bSFrançois Tigeot if (crtc->funcs->early_unregister)
1821dedbd3bSFrançois Tigeot crtc->funcs->early_unregister(crtc);
1834be47400SFrançois Tigeot drm_debugfs_crtc_remove(crtc);
1841dedbd3bSFrançois Tigeot }
1851dedbd3bSFrançois Tigeot }
1861dedbd3bSFrançois Tigeot
drm_crtc_crc_init(struct drm_crtc * crtc)1874be47400SFrançois Tigeot static int drm_crtc_crc_init(struct drm_crtc *crtc)
1884be47400SFrançois Tigeot {
1894be47400SFrançois Tigeot #ifdef CONFIG_DEBUG_FS
1904be47400SFrançois Tigeot spin_lock_init(&crtc->crc.lock);
1914be47400SFrançois Tigeot init_waitqueue_head(&crtc->crc.wq);
1924be47400SFrançois Tigeot crtc->crc.source = kstrdup("auto", GFP_KERNEL);
1934be47400SFrançois Tigeot if (!crtc->crc.source)
1944be47400SFrançois Tigeot return -ENOMEM;
1954be47400SFrançois Tigeot #endif
1964be47400SFrançois Tigeot return 0;
1974be47400SFrançois Tigeot }
1984be47400SFrançois Tigeot
drm_crtc_crc_fini(struct drm_crtc * crtc)1994be47400SFrançois Tigeot static void drm_crtc_crc_fini(struct drm_crtc *crtc)
2004be47400SFrançois Tigeot {
2014be47400SFrançois Tigeot #ifdef CONFIG_DEBUG_FS
2024be47400SFrançois Tigeot kfree(crtc->crc.source);
2034be47400SFrançois Tigeot #endif
2044be47400SFrançois Tigeot }
2054be47400SFrançois Tigeot
2064be47400SFrançois Tigeot static const struct dma_fence_ops drm_crtc_fence_ops;
2074be47400SFrançois Tigeot
fence_to_crtc(struct dma_fence * fence)2084be47400SFrançois Tigeot static struct drm_crtc *fence_to_crtc(struct dma_fence *fence)
2094be47400SFrançois Tigeot {
2104be47400SFrançois Tigeot BUG_ON(fence->ops != &drm_crtc_fence_ops);
2114be47400SFrançois Tigeot return container_of(fence->lock, struct drm_crtc, fence_lock);
2124be47400SFrançois Tigeot }
2134be47400SFrançois Tigeot
drm_crtc_fence_get_driver_name(struct dma_fence * fence)2144be47400SFrançois Tigeot static const char *drm_crtc_fence_get_driver_name(struct dma_fence *fence)
2154be47400SFrançois Tigeot {
2164be47400SFrançois Tigeot struct drm_crtc *crtc = fence_to_crtc(fence);
2174be47400SFrançois Tigeot
2184be47400SFrançois Tigeot return crtc->dev->driver->name;
2194be47400SFrançois Tigeot }
2204be47400SFrançois Tigeot
drm_crtc_fence_get_timeline_name(struct dma_fence * fence)2214be47400SFrançois Tigeot static const char *drm_crtc_fence_get_timeline_name(struct dma_fence *fence)
2224be47400SFrançois Tigeot {
2234be47400SFrançois Tigeot struct drm_crtc *crtc = fence_to_crtc(fence);
2244be47400SFrançois Tigeot
2254be47400SFrançois Tigeot return crtc->timeline_name;
2264be47400SFrançois Tigeot }
2274be47400SFrançois Tigeot
drm_crtc_fence_enable_signaling(struct dma_fence * fence)2284be47400SFrançois Tigeot static bool drm_crtc_fence_enable_signaling(struct dma_fence *fence)
2294be47400SFrançois Tigeot {
2304be47400SFrançois Tigeot return true;
2314be47400SFrançois Tigeot }
2324be47400SFrançois Tigeot
2334be47400SFrançois Tigeot static const struct dma_fence_ops drm_crtc_fence_ops = {
2344be47400SFrançois Tigeot .get_driver_name = drm_crtc_fence_get_driver_name,
2354be47400SFrançois Tigeot .get_timeline_name = drm_crtc_fence_get_timeline_name,
2364be47400SFrançois Tigeot .enable_signaling = drm_crtc_fence_enable_signaling,
2374be47400SFrançois Tigeot .wait = dma_fence_default_wait,
2384be47400SFrançois Tigeot };
2394be47400SFrançois Tigeot
drm_crtc_create_fence(struct drm_crtc * crtc)2404be47400SFrançois Tigeot struct dma_fence *drm_crtc_create_fence(struct drm_crtc *crtc)
2414be47400SFrançois Tigeot {
2424be47400SFrançois Tigeot struct dma_fence *fence;
2434be47400SFrançois Tigeot
2444be47400SFrançois Tigeot fence = kzalloc(sizeof(*fence), GFP_KERNEL);
2454be47400SFrançois Tigeot if (!fence)
2464be47400SFrançois Tigeot return NULL;
2474be47400SFrançois Tigeot
2484be47400SFrançois Tigeot dma_fence_init(fence, &drm_crtc_fence_ops, &crtc->fence_lock,
2494be47400SFrançois Tigeot crtc->fence_context, ++crtc->fence_seqno);
2504be47400SFrançois Tigeot
2514be47400SFrançois Tigeot return fence;
2524be47400SFrançois Tigeot }
2534be47400SFrançois Tigeot
2545718399fSFrançois Tigeot /**
255ba55f2f5SFrançois Tigeot * drm_crtc_init_with_planes - Initialise a new CRTC object with
256ba55f2f5SFrançois Tigeot * specified primary and cursor planes.
2575718399fSFrançois Tigeot * @dev: DRM device
2585718399fSFrançois Tigeot * @crtc: CRTC object to init
259ba55f2f5SFrançois Tigeot * @primary: Primary plane for CRTC
260ba55f2f5SFrançois Tigeot * @cursor: Cursor plane for CRTC
2615718399fSFrançois Tigeot * @funcs: callbacks for the new CRTC
262aee94f86SFrançois Tigeot * @name: printf style format string for the CRTC name, or NULL for default name
2635718399fSFrançois Tigeot *
2641dedbd3bSFrançois Tigeot * Inits a new object created as base part of a driver crtc object. Drivers
2651dedbd3bSFrançois Tigeot * should use this function instead of drm_crtc_init(), which is only provided
2661dedbd3bSFrançois Tigeot * for backwards compatibility with drivers which do not yet support universal
2671dedbd3bSFrançois Tigeot * planes). For really simple hardware which has only 1 plane look at
2681dedbd3bSFrançois Tigeot * drm_simple_display_pipe_init() instead.
2695718399fSFrançois Tigeot *
270ba55f2f5SFrançois Tigeot * Returns:
2715718399fSFrançois Tigeot * Zero on success, error code on failure.
2725718399fSFrançois Tigeot */
drm_crtc_init_with_planes(struct drm_device * dev,struct drm_crtc * crtc,struct drm_plane * primary,struct drm_plane * cursor,const struct drm_crtc_funcs * funcs,const char * name,...)273ba55f2f5SFrançois Tigeot int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc,
274ba55f2f5SFrançois Tigeot struct drm_plane *primary,
27524edb884SFrançois Tigeot struct drm_plane *cursor,
276aee94f86SFrançois Tigeot const struct drm_crtc_funcs *funcs,
277aee94f86SFrançois Tigeot const char *name, ...)
2785718399fSFrançois Tigeot {
279ba55f2f5SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config;
2805718399fSFrançois Tigeot int ret;
2815718399fSFrançois Tigeot
282477eb7f9SFrançois Tigeot WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY);
283477eb7f9SFrançois Tigeot WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR);
284477eb7f9SFrançois Tigeot
2855718399fSFrançois Tigeot crtc->dev = dev;
2865718399fSFrançois Tigeot crtc->funcs = funcs;
2875718399fSFrançois Tigeot
2881dedbd3bSFrançois Tigeot INIT_LIST_HEAD(&crtc->commit_list);
2899a49c39cSFrançois Tigeot lockinit(&crtc->commit_lock, "dccl", 0, 0);
2901dedbd3bSFrançois Tigeot
291ba55f2f5SFrançois Tigeot drm_modeset_lock_init(&crtc->mutex);
292a85cb24fSFrançois Tigeot ret = drm_mode_object_add(dev, &crtc->base, DRM_MODE_OBJECT_CRTC);
2935718399fSFrançois Tigeot if (ret)
29419c468b4SFrançois Tigeot return ret;
2955718399fSFrançois Tigeot
296aee94f86SFrançois Tigeot if (name) {
2979286b91eSSascha Wildner va_list ap;
298aee94f86SFrançois Tigeot
2999286b91eSSascha Wildner va_start(ap, name);
300aee94f86SFrançois Tigeot crtc->name = kvasprintf(GFP_KERNEL, name, ap);
3019286b91eSSascha Wildner va_end(ap);
302aee94f86SFrançois Tigeot } else {
303aee94f86SFrançois Tigeot crtc->name = kasprintf(GFP_KERNEL, "crtc-%d",
304aee94f86SFrançois Tigeot drm_num_crtcs(dev));
305aee94f86SFrançois Tigeot }
306aee94f86SFrançois Tigeot if (!crtc->name) {
3078621f407SFrançois Tigeot drm_mode_object_unregister(dev, &crtc->base);
308aee94f86SFrançois Tigeot return -ENOMEM;
309aee94f86SFrançois Tigeot }
310aee94f86SFrançois Tigeot
3114be47400SFrançois Tigeot crtc->fence_context = dma_fence_context_alloc(1);
3124be47400SFrançois Tigeot lockinit(&crtc->fence_lock, "drmcfl", 0, 0);
3134be47400SFrançois Tigeot snprintf(crtc->timeline_name, sizeof(crtc->timeline_name),
3144be47400SFrançois Tigeot "CRTC:%d-%s", crtc->base.id, crtc->name);
3154be47400SFrançois Tigeot
316b5162e19SFrançois Tigeot crtc->base.properties = &crtc->properties;
317b5162e19SFrançois Tigeot
318ba55f2f5SFrançois Tigeot list_add_tail(&crtc->head, &config->crtc_list);
3191dedbd3bSFrançois Tigeot crtc->index = config->num_crtc++;
320ba55f2f5SFrançois Tigeot
321ba55f2f5SFrançois Tigeot crtc->primary = primary;
32224edb884SFrançois Tigeot crtc->cursor = cursor;
3234be47400SFrançois Tigeot if (primary && !primary->possible_crtcs)
324ba55f2f5SFrançois Tigeot primary->possible_crtcs = 1 << drm_crtc_index(crtc);
3254be47400SFrançois Tigeot if (cursor && !cursor->possible_crtcs)
32624edb884SFrançois Tigeot cursor->possible_crtcs = 1 << drm_crtc_index(crtc);
327b5162e19SFrançois Tigeot
3284be47400SFrançois Tigeot ret = drm_crtc_crc_init(crtc);
3294be47400SFrançois Tigeot if (ret) {
3304be47400SFrançois Tigeot drm_mode_object_unregister(dev, &crtc->base);
3314be47400SFrançois Tigeot return ret;
3324be47400SFrançois Tigeot }
3334be47400SFrançois Tigeot
33419c468b4SFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_ATOMIC)) {
33519c468b4SFrançois Tigeot drm_object_attach_property(&crtc->base, config->prop_active, 0);
33619c468b4SFrançois Tigeot drm_object_attach_property(&crtc->base, config->prop_mode_id, 0);
3374be47400SFrançois Tigeot drm_object_attach_property(&crtc->base,
3384be47400SFrançois Tigeot config->prop_out_fence_ptr, 0);
33919c468b4SFrançois Tigeot }
3405718399fSFrançois Tigeot
34119c468b4SFrançois Tigeot return 0;
3425718399fSFrançois Tigeot }
343ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init_with_planes);
3445718399fSFrançois Tigeot
3455718399fSFrançois Tigeot /**
34606fede5aSFrançois Tigeot * drm_crtc_cleanup - Clean up the core crtc usage
3475718399fSFrançois Tigeot * @crtc: CRTC to cleanup
3485718399fSFrançois Tigeot *
34906fede5aSFrançois Tigeot * This function cleans up @crtc and removes it from the DRM mode setting
35006fede5aSFrançois Tigeot * core. Note that the function does *not* free the crtc structure itself,
35106fede5aSFrançois Tigeot * this is the responsibility of the caller.
3525718399fSFrançois Tigeot */
drm_crtc_cleanup(struct drm_crtc * crtc)3535718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc)
3545718399fSFrançois Tigeot {
3555718399fSFrançois Tigeot struct drm_device *dev = crtc->dev;
3565718399fSFrançois Tigeot
3571dedbd3bSFrançois Tigeot /* Note that the crtc_list is considered to be static; should we
3581dedbd3bSFrançois Tigeot * remove the drm_crtc at runtime we would have to decrement all
3591dedbd3bSFrançois Tigeot * the indices on the drm_crtc after us in the crtc_list.
3601dedbd3bSFrançois Tigeot */
3611dedbd3bSFrançois Tigeot
3624be47400SFrançois Tigeot drm_crtc_crc_fini(crtc);
3634be47400SFrançois Tigeot
3644dbb207bSFrançois Tigeot kfree(crtc->gamma_store);
3655718399fSFrançois Tigeot crtc->gamma_store = NULL;
3665718399fSFrançois Tigeot
367ba55f2f5SFrançois Tigeot drm_modeset_lock_fini(&crtc->mutex);
368ba55f2f5SFrançois Tigeot
3698621f407SFrançois Tigeot drm_mode_object_unregister(dev, &crtc->base);
3705718399fSFrançois Tigeot list_del(&crtc->head);
3715718399fSFrançois Tigeot dev->mode_config.num_crtc--;
3722c9916cdSFrançois Tigeot
3732c9916cdSFrançois Tigeot WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state);
3742c9916cdSFrançois Tigeot if (crtc->state && crtc->funcs->atomic_destroy_state)
3752c9916cdSFrançois Tigeot crtc->funcs->atomic_destroy_state(crtc, crtc->state);
3762c9916cdSFrançois Tigeot
377aee94f86SFrançois Tigeot kfree(crtc->name);
378aee94f86SFrançois Tigeot
3792c9916cdSFrançois Tigeot memset(crtc, 0, sizeof(*crtc));
3805718399fSFrançois Tigeot }
381b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup);
3825718399fSFrançois Tigeot
3835718399fSFrançois Tigeot /**
384c7dca6a7SFrançois Tigeot * drm_connector_index - find the index of a registered connector
385c7dca6a7SFrançois Tigeot * @connector: connector to find index for
386c7dca6a7SFrançois Tigeot *
387c7dca6a7SFrançois Tigeot * Given a registered connector, return the index of that connector within a DRM
388c7dca6a7SFrançois Tigeot * device's list of connectors.
389c7dca6a7SFrançois Tigeot */
drm_connector_index(struct drm_connector * connector)390c7dca6a7SFrançois Tigeot unsigned int drm_connector_index(struct drm_connector *connector)
391c7dca6a7SFrançois Tigeot {
392c7dca6a7SFrançois Tigeot unsigned int index = 0;
393c7dca6a7SFrançois Tigeot struct drm_connector *tmp;
394c7dca6a7SFrançois Tigeot struct drm_mode_config *config = &connector->dev->mode_config;
395*3f2dd94aSFrançois Tigeot struct drm_connector_list_iter conn_iter;
396c7dca6a7SFrançois Tigeot
397c7dca6a7SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&config->connection_mutex));
398c7dca6a7SFrançois Tigeot
399*3f2dd94aSFrançois Tigeot drm_connector_list_iter_begin(connector->dev, &conn_iter);
400*3f2dd94aSFrançois Tigeot drm_for_each_connector_iter(tmp, &conn_iter) {
401*3f2dd94aSFrançois Tigeot if (tmp == connector) {
402*3f2dd94aSFrançois Tigeot drm_connector_list_iter_end(&conn_iter);
403c7dca6a7SFrançois Tigeot return index;
404*3f2dd94aSFrançois Tigeot }
405c7dca6a7SFrançois Tigeot
406c7dca6a7SFrançois Tigeot index++;
407c7dca6a7SFrançois Tigeot }
408*3f2dd94aSFrançois Tigeot drm_connector_list_iter_end(&conn_iter);
409c7dca6a7SFrançois Tigeot
410c7dca6a7SFrançois Tigeot BUG();
411c7dca6a7SFrançois Tigeot }
412c7dca6a7SFrançois Tigeot EXPORT_SYMBOL(drm_connector_index);
413c7dca6a7SFrançois Tigeot
4145718399fSFrançois Tigeot /**
4155718399fSFrançois Tigeot * drm_mode_getcrtc - get CRTC configuration
4164dbb207bSFrançois Tigeot * @dev: drm device for the ioctl
4174dbb207bSFrançois Tigeot * @data: data pointer for the ioctl
4184dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call
4195718399fSFrançois Tigeot *
4205718399fSFrançois Tigeot * Construct a CRTC configuration structure to return to the user.
4215718399fSFrançois Tigeot *
4225718399fSFrançois Tigeot * Called by the user via ioctl.
4235718399fSFrançois Tigeot *
424ba55f2f5SFrançois Tigeot * Returns:
4252c9916cdSFrançois Tigeot * Zero on success, negative errno on failure.
4265718399fSFrançois Tigeot */
drm_mode_getcrtc(struct drm_device * dev,void * data,struct drm_file * file_priv)4275718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev,
4285718399fSFrançois Tigeot void *data, struct drm_file *file_priv)
4295718399fSFrançois Tigeot {
4305718399fSFrançois Tigeot struct drm_mode_crtc *crtc_resp = data;
4315718399fSFrançois Tigeot struct drm_crtc *crtc;
4325718399fSFrançois Tigeot
4335718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
434b5162e19SFrançois Tigeot return -EINVAL;
4355718399fSFrançois Tigeot
436*3f2dd94aSFrançois Tigeot crtc = drm_crtc_find(dev, file_priv, crtc_resp->crtc_id);
4372c9916cdSFrançois Tigeot if (!crtc)
4382c9916cdSFrançois Tigeot return -ENOENT;
4395718399fSFrançois Tigeot
4405718399fSFrançois Tigeot crtc_resp->gamma_size = crtc->gamma_size;
441a85cb24fSFrançois Tigeot
442a85cb24fSFrançois Tigeot drm_modeset_lock(&crtc->primary->mutex, NULL);
443a85cb24fSFrançois Tigeot if (crtc->primary->state && crtc->primary->state->fb)
444a85cb24fSFrançois Tigeot crtc_resp->fb_id = crtc->primary->state->fb->base.id;
445a85cb24fSFrançois Tigeot else if (!crtc->primary->state && crtc->primary->fb)
446ba55f2f5SFrançois Tigeot crtc_resp->fb_id = crtc->primary->fb->base.id;
4475718399fSFrançois Tigeot else
4485718399fSFrançois Tigeot crtc_resp->fb_id = 0;
4495718399fSFrançois Tigeot
450a85cb24fSFrançois Tigeot if (crtc->primary->state) {
451477eb7f9SFrançois Tigeot crtc_resp->x = crtc->primary->state->src_x >> 16;
452477eb7f9SFrançois Tigeot crtc_resp->y = crtc->primary->state->src_y >> 16;
453a85cb24fSFrançois Tigeot }
454a85cb24fSFrançois Tigeot drm_modeset_unlock(&crtc->primary->mutex);
455a85cb24fSFrançois Tigeot
456a85cb24fSFrançois Tigeot drm_modeset_lock(&crtc->mutex, NULL);
457a85cb24fSFrançois Tigeot if (crtc->state) {
458477eb7f9SFrançois Tigeot if (crtc->state->enable) {
45919c468b4SFrançois Tigeot drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode);
460477eb7f9SFrançois Tigeot crtc_resp->mode_valid = 1;
4615718399fSFrançois Tigeot
462477eb7f9SFrançois Tigeot } else {
463477eb7f9SFrançois Tigeot crtc_resp->mode_valid = 0;
464477eb7f9SFrançois Tigeot }
465477eb7f9SFrançois Tigeot } else {
466477eb7f9SFrançois Tigeot crtc_resp->x = crtc->x;
467477eb7f9SFrançois Tigeot crtc_resp->y = crtc->y;
468477eb7f9SFrançois Tigeot if (crtc->enabled) {
46919c468b4SFrançois Tigeot drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode);
4705718399fSFrançois Tigeot crtc_resp->mode_valid = 1;
4715718399fSFrançois Tigeot
4725718399fSFrançois Tigeot } else {
4735718399fSFrançois Tigeot crtc_resp->mode_valid = 0;
4745718399fSFrançois Tigeot }
475477eb7f9SFrançois Tigeot }
476a85cb24fSFrançois Tigeot drm_modeset_unlock(&crtc->mutex);
4775718399fSFrançois Tigeot
4782c9916cdSFrançois Tigeot return 0;
4795718399fSFrançois Tigeot }
4805718399fSFrançois Tigeot
__drm_mode_set_config_internal(struct drm_mode_set * set,struct drm_modeset_acquire_ctx * ctx)481a85cb24fSFrançois Tigeot static int __drm_mode_set_config_internal(struct drm_mode_set *set,
482a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx *ctx)
4834dbb207bSFrançois Tigeot {
4844dbb207bSFrançois Tigeot struct drm_crtc *crtc = set->crtc;
48506fede5aSFrançois Tigeot struct drm_framebuffer *fb;
48606fede5aSFrançois Tigeot struct drm_crtc *tmp;
4874dbb207bSFrançois Tigeot int ret;
4884dbb207bSFrançois Tigeot
48906fede5aSFrançois Tigeot /*
49006fede5aSFrançois Tigeot * NOTE: ->set_config can also disable other crtcs (if we steal all
49106fede5aSFrançois Tigeot * connectors from it), hence we need to refcount the fbs across all
49206fede5aSFrançois Tigeot * crtcs. Atomic modeset will have saner semantics ...
49306fede5aSFrançois Tigeot */
494a05eeebfSFrançois Tigeot drm_for_each_crtc(tmp, crtc->dev)
4951b13d190SFrançois Tigeot tmp->primary->old_fb = tmp->primary->fb;
49606fede5aSFrançois Tigeot
4974dbb207bSFrançois Tigeot fb = set->fb;
4984dbb207bSFrançois Tigeot
499a85cb24fSFrançois Tigeot ret = crtc->funcs->set_config(set, ctx);
5004dbb207bSFrançois Tigeot if (ret == 0) {
501ba55f2f5SFrançois Tigeot crtc->primary->crtc = crtc;
502ba55f2f5SFrançois Tigeot crtc->primary->fb = fb;
50306fede5aSFrançois Tigeot }
50406fede5aSFrançois Tigeot
505a05eeebfSFrançois Tigeot drm_for_each_crtc(tmp, crtc->dev) {
506ba55f2f5SFrançois Tigeot if (tmp->primary->fb)
507a85cb24fSFrançois Tigeot drm_framebuffer_get(tmp->primary->fb);
5081b13d190SFrançois Tigeot if (tmp->primary->old_fb)
509a85cb24fSFrançois Tigeot drm_framebuffer_put(tmp->primary->old_fb);
5101b13d190SFrançois Tigeot tmp->primary->old_fb = NULL;
5114dbb207bSFrançois Tigeot }
5124dbb207bSFrançois Tigeot
5134dbb207bSFrançois Tigeot return ret;
5144dbb207bSFrançois Tigeot }
515ba55f2f5SFrançois Tigeot /**
516a85cb24fSFrançois Tigeot * drm_mode_set_config_internal - helper to call &drm_mode_config_funcs.set_config
517a85cb24fSFrançois Tigeot * @set: modeset config to set
5182c9916cdSFrançois Tigeot *
519a85cb24fSFrançois Tigeot * This is a little helper to wrap internal calls to the
520a85cb24fSFrançois Tigeot * &drm_mode_config_funcs.set_config driver interface. The only thing it adds is
521a85cb24fSFrançois Tigeot * correct refcounting dance.
522a85cb24fSFrançois Tigeot *
523a85cb24fSFrançois Tigeot * This should only be used by non-atomic legacy drivers.
524a85cb24fSFrançois Tigeot *
525a85cb24fSFrançois Tigeot * Returns:
526a85cb24fSFrançois Tigeot * Zero on success, negative errno on failure.
5272c9916cdSFrançois Tigeot */
drm_mode_set_config_internal(struct drm_mode_set * set)528a85cb24fSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set)
5292c9916cdSFrançois Tigeot {
530a85cb24fSFrançois Tigeot WARN_ON(drm_drv_uses_atomic_modeset(set->crtc->dev));
5312c9916cdSFrançois Tigeot
532a85cb24fSFrançois Tigeot return __drm_mode_set_config_internal(set, NULL);
5332c9916cdSFrançois Tigeot }
534a85cb24fSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal);
5352c9916cdSFrançois Tigeot
5362c9916cdSFrançois Tigeot /**
537ba55f2f5SFrançois Tigeot * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the
538ba55f2f5SFrançois Tigeot * CRTC viewport
539ba55f2f5SFrançois Tigeot * @crtc: CRTC that framebuffer will be displayed on
540ba55f2f5SFrançois Tigeot * @x: x panning
541ba55f2f5SFrançois Tigeot * @y: y panning
542ba55f2f5SFrançois Tigeot * @mode: mode that framebuffer will be displayed under
543ba55f2f5SFrançois Tigeot * @fb: framebuffer to check size of
5449edbd4a0SFrançois Tigeot */
drm_crtc_check_viewport(const struct drm_crtc * crtc,int x,int y,const struct drm_display_mode * mode,const struct drm_framebuffer * fb)545ba55f2f5SFrançois Tigeot int drm_crtc_check_viewport(const struct drm_crtc *crtc,
5469edbd4a0SFrançois Tigeot int x, int y,
5479edbd4a0SFrançois Tigeot const struct drm_display_mode *mode,
5489edbd4a0SFrançois Tigeot const struct drm_framebuffer *fb)
5499edbd4a0SFrançois Tigeot
5509edbd4a0SFrançois Tigeot {
5519edbd4a0SFrançois Tigeot int hdisplay, vdisplay;
5529edbd4a0SFrançois Tigeot
553a85cb24fSFrançois Tigeot drm_mode_get_hv_timing(mode, &hdisplay, &vdisplay);
5549edbd4a0SFrançois Tigeot
555352ff8bdSFrançois Tigeot if (crtc->state &&
5564be47400SFrançois Tigeot drm_rotation_90_or_270(crtc->primary->state->rotation))
5579edbd4a0SFrançois Tigeot swap(hdisplay, vdisplay);
5589edbd4a0SFrançois Tigeot
5591dedbd3bSFrançois Tigeot return drm_framebuffer_check_src_coords(x << 16, y << 16,
5601dedbd3bSFrançois Tigeot hdisplay << 16, vdisplay << 16,
5611dedbd3bSFrançois Tigeot fb);
5629edbd4a0SFrançois Tigeot }
563ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_check_viewport);
5649edbd4a0SFrançois Tigeot
5654dbb207bSFrançois Tigeot /**
5664dbb207bSFrançois Tigeot * drm_mode_setcrtc - set CRTC configuration
5674dbb207bSFrançois Tigeot * @dev: drm device for the ioctl
5684dbb207bSFrançois Tigeot * @data: data pointer for the ioctl
5694dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call
5705718399fSFrançois Tigeot *
5715718399fSFrançois Tigeot * Build a new CRTC configuration based on user request.
5725718399fSFrançois Tigeot *
5735718399fSFrançois Tigeot * Called by the user via ioctl.
5745718399fSFrançois Tigeot *
575ba55f2f5SFrançois Tigeot * Returns:
5762c9916cdSFrançois Tigeot * Zero on success, negative errno on failure.
5775718399fSFrançois Tigeot */
drm_mode_setcrtc(struct drm_device * dev,void * data,struct drm_file * file_priv)5785718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data,
5795718399fSFrançois Tigeot struct drm_file *file_priv)
5805718399fSFrançois Tigeot {
5815718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config;
5825718399fSFrançois Tigeot struct drm_mode_crtc *crtc_req = data;
5835718399fSFrançois Tigeot struct drm_crtc *crtc;
5845718399fSFrançois Tigeot struct drm_connector **connector_set = NULL, *connector;
5855718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL;
5865718399fSFrançois Tigeot struct drm_display_mode *mode = NULL;
5875718399fSFrançois Tigeot struct drm_mode_set set;
588b5162e19SFrançois Tigeot uint32_t __user *set_connectors_ptr;
589a85cb24fSFrançois Tigeot struct drm_modeset_acquire_ctx ctx;
590b5162e19SFrançois Tigeot int ret;
5915718399fSFrançois Tigeot int i;
5925718399fSFrançois Tigeot
5935718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET))
594b5162e19SFrançois Tigeot return -EINVAL;
5955718399fSFrançois Tigeot
59619c468b4SFrançois Tigeot /*
59719c468b4SFrançois Tigeot * Universal plane src offsets are only 16.16, prevent havoc for
59819c468b4SFrançois Tigeot * drivers using universal plane code internally.
59919c468b4SFrançois Tigeot */
60019c468b4SFrançois Tigeot if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000)
601b5162e19SFrançois Tigeot return -ERANGE;
6025718399fSFrançois Tigeot
603*3f2dd94aSFrançois Tigeot crtc = drm_crtc_find(dev, file_priv, crtc_req->crtc_id);
604ba55f2f5SFrançois Tigeot if (!crtc) {
6055718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id);
606a85cb24fSFrançois Tigeot return -ENOENT;
6075718399fSFrançois Tigeot }
608aee94f86SFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name);
6095718399fSFrançois Tigeot
610a85cb24fSFrançois Tigeot mutex_lock(&crtc->dev->mode_config.mutex);
611*3f2dd94aSFrançois Tigeot drm_modeset_acquire_init(&ctx, DRM_MODESET_ACQUIRE_INTERRUPTIBLE);
612a85cb24fSFrançois Tigeot retry:
613a85cb24fSFrançois Tigeot ret = drm_modeset_lock_all_ctx(crtc->dev, &ctx);
614a85cb24fSFrançois Tigeot if (ret)
615a85cb24fSFrançois Tigeot goto out;
6165718399fSFrançois Tigeot if (crtc_req->mode_valid) {
6175718399fSFrançois Tigeot /* If we have a mode we need a framebuffer. */
6185718399fSFrançois Tigeot /* If we pass -1, set the mode with the currently bound fb */
6195718399fSFrançois Tigeot if (crtc_req->fb_id == -1) {
620ba55f2f5SFrançois Tigeot if (!crtc->primary->fb) {
6215718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC doesn't have current FB\n");
6225718399fSFrançois Tigeot ret = -EINVAL;
6235718399fSFrançois Tigeot goto out;
6245718399fSFrançois Tigeot }
625ba55f2f5SFrançois Tigeot fb = crtc->primary->fb;
6264dbb207bSFrançois Tigeot /* Make refcounting symmetric with the lookup path. */
627a85cb24fSFrançois Tigeot drm_framebuffer_get(fb);
6285718399fSFrançois Tigeot } else {
629*3f2dd94aSFrançois Tigeot fb = drm_framebuffer_lookup(dev, file_priv, crtc_req->fb_id);
6304dbb207bSFrançois Tigeot if (!fb) {
6315718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown FB ID%d\n",
6325718399fSFrançois Tigeot crtc_req->fb_id);
6339edbd4a0SFrançois Tigeot ret = -ENOENT;
6345718399fSFrançois Tigeot goto out;
6355718399fSFrançois Tigeot }
6365718399fSFrançois Tigeot }
6375718399fSFrançois Tigeot
6385718399fSFrançois Tigeot mode = drm_mode_create(dev);
6395718399fSFrançois Tigeot if (!mode) {
640b5162e19SFrançois Tigeot ret = -ENOMEM;
6415718399fSFrançois Tigeot goto out;
6425718399fSFrançois Tigeot }
6435718399fSFrançois Tigeot
64419c468b4SFrançois Tigeot ret = drm_mode_convert_umode(mode, &crtc_req->mode);
6455718399fSFrançois Tigeot if (ret) {
6465718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid mode\n");
6475718399fSFrançois Tigeot goto out;
6485718399fSFrançois Tigeot }
6495718399fSFrançois Tigeot
650477eb7f9SFrançois Tigeot /*
651477eb7f9SFrançois Tigeot * Check whether the primary plane supports the fb pixel format.
652477eb7f9SFrançois Tigeot * Drivers not implementing the universal planes API use a
653477eb7f9SFrançois Tigeot * default formats list provided by the DRM core which doesn't
654477eb7f9SFrançois Tigeot * match real hardware capabilities. Skip the check in that
655477eb7f9SFrançois Tigeot * case.
656477eb7f9SFrançois Tigeot */
657477eb7f9SFrançois Tigeot if (!crtc->primary->format_default) {
658477eb7f9SFrançois Tigeot ret = drm_plane_check_pixel_format(crtc->primary,
659a85cb24fSFrançois Tigeot fb->format->format);
660477eb7f9SFrançois Tigeot if (ret) {
6614be47400SFrançois Tigeot struct drm_format_name_buf format_name;
6624be47400SFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n",
663a85cb24fSFrançois Tigeot drm_get_format_name(fb->format->format,
6644be47400SFrançois Tigeot &format_name));
665477eb7f9SFrançois Tigeot goto out;
666477eb7f9SFrançois Tigeot }
667477eb7f9SFrançois Tigeot }
668477eb7f9SFrançois Tigeot
6699edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y,
6709edbd4a0SFrançois Tigeot mode, fb);
6719edbd4a0SFrançois Tigeot if (ret)
6725718399fSFrançois Tigeot goto out;
6739edbd4a0SFrançois Tigeot
6745718399fSFrançois Tigeot }
6755718399fSFrançois Tigeot
6765718399fSFrançois Tigeot if (crtc_req->count_connectors == 0 && mode) {
6775718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is 0 but mode set\n");
678b5162e19SFrançois Tigeot ret = -EINVAL;
6795718399fSFrançois Tigeot goto out;
6805718399fSFrançois Tigeot }
6815718399fSFrançois Tigeot
6825718399fSFrançois Tigeot if (crtc_req->count_connectors > 0 && (!mode || !fb)) {
6835718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n",
6845718399fSFrançois Tigeot crtc_req->count_connectors);
685b5162e19SFrançois Tigeot ret = -EINVAL;
6865718399fSFrançois Tigeot goto out;
6875718399fSFrançois Tigeot }
6885718399fSFrançois Tigeot
6895718399fSFrançois Tigeot if (crtc_req->count_connectors > 0) {
6905718399fSFrançois Tigeot u32 out_id;
6915718399fSFrançois Tigeot
6925718399fSFrançois Tigeot /* Avoid unbounded kernel memory allocation */
6935718399fSFrançois Tigeot if (crtc_req->count_connectors > config->num_connector) {
694b5162e19SFrançois Tigeot ret = -EINVAL;
6955718399fSFrançois Tigeot goto out;
6965718399fSFrançois Tigeot }
6975718399fSFrançois Tigeot
6981dedbd3bSFrançois Tigeot connector_set = kmalloc_array(crtc_req->count_connectors,
6994dbb207bSFrançois Tigeot sizeof(struct drm_connector *),
7001dedbd3bSFrançois Tigeot GFP_KERNEL);
701b5162e19SFrançois Tigeot if (!connector_set) {
702b5162e19SFrançois Tigeot ret = -ENOMEM;
703b5162e19SFrançois Tigeot goto out;
704b5162e19SFrançois Tigeot }
7055718399fSFrançois Tigeot
7065718399fSFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) {
7078621f407SFrançois Tigeot connector_set[i] = NULL;
708b5162e19SFrançois Tigeot set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr;
709b5162e19SFrançois Tigeot if (get_user(out_id, &set_connectors_ptr[i])) {
710b5162e19SFrançois Tigeot ret = -EFAULT;
7115718399fSFrançois Tigeot goto out;
7125718399fSFrançois Tigeot }
7135718399fSFrançois Tigeot
714*3f2dd94aSFrançois Tigeot connector = drm_connector_lookup(dev, file_priv, out_id);
715ba55f2f5SFrançois Tigeot if (!connector) {
7165718399fSFrançois Tigeot DRM_DEBUG_KMS("Connector id %d unknown\n",
7175718399fSFrançois Tigeot out_id);
7189edbd4a0SFrançois Tigeot ret = -ENOENT;
7195718399fSFrançois Tigeot goto out;
7205718399fSFrançois Tigeot }
7215718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n",
7225718399fSFrançois Tigeot connector->base.id,
723ba55f2f5SFrançois Tigeot connector->name);
7245718399fSFrançois Tigeot
7255718399fSFrançois Tigeot connector_set[i] = connector;
7265718399fSFrançois Tigeot }
7275718399fSFrançois Tigeot }
7285718399fSFrançois Tigeot
7295718399fSFrançois Tigeot set.crtc = crtc;
7305718399fSFrançois Tigeot set.x = crtc_req->x;
7315718399fSFrançois Tigeot set.y = crtc_req->y;
7325718399fSFrançois Tigeot set.mode = mode;
7335718399fSFrançois Tigeot set.connectors = connector_set;
7345718399fSFrançois Tigeot set.num_connectors = crtc_req->count_connectors;
7355718399fSFrançois Tigeot set.fb = fb;
736a85cb24fSFrançois Tigeot ret = __drm_mode_set_config_internal(&set, &ctx);
7375718399fSFrançois Tigeot
7385718399fSFrançois Tigeot out:
7394dbb207bSFrançois Tigeot if (fb)
740a85cb24fSFrançois Tigeot drm_framebuffer_put(fb);
7414dbb207bSFrançois Tigeot
7428621f407SFrançois Tigeot if (connector_set) {
7438621f407SFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) {
7448621f407SFrançois Tigeot if (connector_set[i])
745a85cb24fSFrançois Tigeot drm_connector_put(connector_set[i]);
7468621f407SFrançois Tigeot }
7478621f407SFrançois Tigeot }
7484dbb207bSFrançois Tigeot kfree(connector_set);
7495718399fSFrançois Tigeot drm_mode_destroy(dev, mode);
750a85cb24fSFrançois Tigeot if (ret == -EDEADLK) {
751*3f2dd94aSFrançois Tigeot ret = drm_modeset_backoff(&ctx);
752*3f2dd94aSFrançois Tigeot if (!ret)
753a85cb24fSFrançois Tigeot goto retry;
754a85cb24fSFrançois Tigeot }
755a85cb24fSFrançois Tigeot drm_modeset_drop_locks(&ctx);
756a85cb24fSFrançois Tigeot drm_modeset_acquire_fini(&ctx);
757a85cb24fSFrançois Tigeot mutex_unlock(&crtc->dev->mode_config.mutex);
758a85cb24fSFrançois Tigeot
7595718399fSFrançois Tigeot return ret;
7605718399fSFrançois Tigeot }
7615718399fSFrançois Tigeot
drm_mode_crtc_set_obj_prop(struct drm_mode_object * obj,struct drm_property * property,uint64_t value)7621dedbd3bSFrançois Tigeot int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj,
763b5162e19SFrançois Tigeot struct drm_property *property,
764b5162e19SFrançois Tigeot uint64_t value)
765b5162e19SFrançois Tigeot {
766b5162e19SFrançois Tigeot int ret = -EINVAL;
767b5162e19SFrançois Tigeot struct drm_crtc *crtc = obj_to_crtc(obj);
768b5162e19SFrançois Tigeot
769b5162e19SFrançois Tigeot if (crtc->funcs->set_property)
770b5162e19SFrançois Tigeot ret = crtc->funcs->set_property(crtc, property, value);
771b5162e19SFrançois Tigeot if (!ret)
772b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value);
773b5162e19SFrançois Tigeot
774b5162e19SFrançois Tigeot return ret;
775b5162e19SFrançois Tigeot }
776