11dedbd3bSFrançois Tigeot /* 21dedbd3bSFrançois Tigeot * Copyright (c) 2016 Intel Corporation 31dedbd3bSFrançois Tigeot * 41dedbd3bSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its 51dedbd3bSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that 61dedbd3bSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright 71dedbd3bSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and 81dedbd3bSFrançois Tigeot * that the name of the copyright holders not be used in advertising or 91dedbd3bSFrançois Tigeot * publicity pertaining to distribution of the software without specific, 101dedbd3bSFrançois Tigeot * written prior permission. The copyright holders make no representations 111dedbd3bSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as 121dedbd3bSFrançois Tigeot * is" without express or implied warranty. 131dedbd3bSFrançois Tigeot * 141dedbd3bSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 151dedbd3bSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 161dedbd3bSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 171dedbd3bSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 181dedbd3bSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 191dedbd3bSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 201dedbd3bSFrançois Tigeot * OF THIS SOFTWARE. 211dedbd3bSFrançois Tigeot */ 221dedbd3bSFrançois Tigeot 231dedbd3bSFrançois Tigeot #include <drm/drmP.h> 241dedbd3bSFrançois Tigeot #include <drm/drm_plane.h> 251dedbd3bSFrançois Tigeot 261dedbd3bSFrançois Tigeot #include "drm_crtc_internal.h" 271dedbd3bSFrançois Tigeot 281dedbd3bSFrançois Tigeot /** 291dedbd3bSFrançois Tigeot * DOC: overview 301dedbd3bSFrançois Tigeot * 311dedbd3bSFrançois Tigeot * A plane represents an image source that can be blended with or overlayed on 321dedbd3bSFrançois Tigeot * top of a CRTC during the scanout process. Planes take their input data from a 331dedbd3bSFrançois Tigeot * &drm_framebuffer object. The plane itself specifies the cropping and scaling 341dedbd3bSFrançois Tigeot * of that image, and where it is placed on the visible are of a display 351dedbd3bSFrançois Tigeot * pipeline, represented by &drm_crtc. A plane can also have additional 361dedbd3bSFrançois Tigeot * properties that specify how the pixels are positioned and blended, like 371dedbd3bSFrançois Tigeot * rotation or Z-position. All these properties are stored in &drm_plane_state. 381dedbd3bSFrançois Tigeot * 391dedbd3bSFrançois Tigeot * To create a plane, a KMS drivers allocates and zeroes an instances of 401dedbd3bSFrançois Tigeot * struct &drm_plane (possibly as part of a larger structure) and registers it 411dedbd3bSFrançois Tigeot * with a call to drm_universal_plane_init(). 421dedbd3bSFrançois Tigeot * 431dedbd3bSFrançois Tigeot * Cursor and overlay planes are optional. All drivers should provide one 441dedbd3bSFrançois Tigeot * primary plane per CRTC to avoid surprising userspace too much. See enum 451dedbd3bSFrançois Tigeot * &drm_plane_type for a more in-depth discussion of these special uapi-relevant 461dedbd3bSFrançois Tigeot * plane types. Special planes are associated with their CRTC by calling 471dedbd3bSFrançois Tigeot * drm_crtc_init_with_planes(). 481dedbd3bSFrançois Tigeot * 491dedbd3bSFrançois Tigeot * The type of a plane is exposed in the immutable "type" enumeration property, 501dedbd3bSFrançois Tigeot * which has one of the following values: "Overlay", "Primary", "Cursor". 511dedbd3bSFrançois Tigeot */ 521dedbd3bSFrançois Tigeot 531dedbd3bSFrançois Tigeot static unsigned int drm_num_planes(struct drm_device *dev) 541dedbd3bSFrançois Tigeot { 551dedbd3bSFrançois Tigeot unsigned int num = 0; 561dedbd3bSFrançois Tigeot struct drm_plane *tmp; 571dedbd3bSFrançois Tigeot 581dedbd3bSFrançois Tigeot drm_for_each_plane(tmp, dev) { 591dedbd3bSFrançois Tigeot num++; 601dedbd3bSFrançois Tigeot } 611dedbd3bSFrançois Tigeot 621dedbd3bSFrançois Tigeot return num; 631dedbd3bSFrançois Tigeot } 641dedbd3bSFrançois Tigeot 651dedbd3bSFrançois Tigeot /** 661dedbd3bSFrançois Tigeot * drm_universal_plane_init - Initialize a new universal plane object 671dedbd3bSFrançois Tigeot * @dev: DRM device 681dedbd3bSFrançois Tigeot * @plane: plane object to init 691dedbd3bSFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 701dedbd3bSFrançois Tigeot * @funcs: callbacks for the new plane 711dedbd3bSFrançois Tigeot * @formats: array of supported formats (DRM_FORMAT\_\*) 721dedbd3bSFrançois Tigeot * @format_count: number of elements in @formats 731dedbd3bSFrançois Tigeot * @type: type of plane (overlay, primary, cursor) 741dedbd3bSFrançois Tigeot * @name: printf style format string for the plane name, or NULL for default name 751dedbd3bSFrançois Tigeot * 761dedbd3bSFrançois Tigeot * Initializes a plane object of type @type. 771dedbd3bSFrançois Tigeot * 781dedbd3bSFrançois Tigeot * Returns: 791dedbd3bSFrançois Tigeot * Zero on success, error code on failure. 801dedbd3bSFrançois Tigeot */ 811dedbd3bSFrançois Tigeot int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, 82*4be47400SFrançois Tigeot uint32_t possible_crtcs, 831dedbd3bSFrançois Tigeot const struct drm_plane_funcs *funcs, 841dedbd3bSFrançois Tigeot const uint32_t *formats, unsigned int format_count, 851dedbd3bSFrançois Tigeot enum drm_plane_type type, 861dedbd3bSFrançois Tigeot const char *name, ...) 871dedbd3bSFrançois Tigeot { 881dedbd3bSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 891dedbd3bSFrançois Tigeot int ret; 901dedbd3bSFrançois Tigeot 911dedbd3bSFrançois Tigeot ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 921dedbd3bSFrançois Tigeot if (ret) 931dedbd3bSFrançois Tigeot return ret; 941dedbd3bSFrançois Tigeot 951dedbd3bSFrançois Tigeot drm_modeset_lock_init(&plane->mutex); 961dedbd3bSFrançois Tigeot 971dedbd3bSFrançois Tigeot plane->base.properties = &plane->properties; 981dedbd3bSFrançois Tigeot plane->dev = dev; 991dedbd3bSFrançois Tigeot plane->funcs = funcs; 1001dedbd3bSFrançois Tigeot plane->format_types = kmalloc_array(format_count, sizeof(uint32_t), 1011dedbd3bSFrançois Tigeot GFP_KERNEL); 1021dedbd3bSFrançois Tigeot if (!plane->format_types) { 1031dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("out of memory when allocating plane\n"); 1041dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 1051dedbd3bSFrançois Tigeot return -ENOMEM; 1061dedbd3bSFrançois Tigeot } 1071dedbd3bSFrançois Tigeot 1081dedbd3bSFrançois Tigeot if (name) { 1091dedbd3bSFrançois Tigeot va_list ap; 1101dedbd3bSFrançois Tigeot 1111dedbd3bSFrançois Tigeot va_start(ap, name); 1121dedbd3bSFrançois Tigeot plane->name = kvasprintf(GFP_KERNEL, name, ap); 1131dedbd3bSFrançois Tigeot va_end(ap); 1141dedbd3bSFrançois Tigeot } else { 1151dedbd3bSFrançois Tigeot plane->name = kasprintf(GFP_KERNEL, "plane-%d", 1161dedbd3bSFrançois Tigeot drm_num_planes(dev)); 1171dedbd3bSFrançois Tigeot } 1181dedbd3bSFrançois Tigeot if (!plane->name) { 1191dedbd3bSFrançois Tigeot kfree(plane->format_types); 1201dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 1211dedbd3bSFrançois Tigeot return -ENOMEM; 1221dedbd3bSFrançois Tigeot } 1231dedbd3bSFrançois Tigeot 1241dedbd3bSFrançois Tigeot memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 1251dedbd3bSFrançois Tigeot plane->format_count = format_count; 1261dedbd3bSFrançois Tigeot plane->possible_crtcs = possible_crtcs; 1271dedbd3bSFrançois Tigeot plane->type = type; 1281dedbd3bSFrançois Tigeot 1291dedbd3bSFrançois Tigeot list_add_tail(&plane->head, &config->plane_list); 1301dedbd3bSFrançois Tigeot plane->index = config->num_total_plane++; 1311dedbd3bSFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 1321dedbd3bSFrançois Tigeot config->num_overlay_plane++; 1331dedbd3bSFrançois Tigeot 1341dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, 1351dedbd3bSFrançois Tigeot config->plane_type_property, 1361dedbd3bSFrançois Tigeot plane->type); 1371dedbd3bSFrançois Tigeot 1381dedbd3bSFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 1391dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_fb_id, 0); 140*4be47400SFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_in_fence_fd, -1); 1411dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); 1421dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); 1431dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); 1441dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); 1451dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); 1461dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_x, 0); 1471dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_y, 0); 1481dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_w, 0); 1491dedbd3bSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_h, 0); 1501dedbd3bSFrançois Tigeot } 1511dedbd3bSFrançois Tigeot 1521dedbd3bSFrançois Tigeot return 0; 1531dedbd3bSFrançois Tigeot } 1541dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_universal_plane_init); 1551dedbd3bSFrançois Tigeot 1561dedbd3bSFrançois Tigeot int drm_plane_register_all(struct drm_device *dev) 1571dedbd3bSFrançois Tigeot { 1581dedbd3bSFrançois Tigeot struct drm_plane *plane; 1591dedbd3bSFrançois Tigeot int ret = 0; 1601dedbd3bSFrançois Tigeot 1611dedbd3bSFrançois Tigeot drm_for_each_plane(plane, dev) { 1621dedbd3bSFrançois Tigeot if (plane->funcs->late_register) 1631dedbd3bSFrançois Tigeot ret = plane->funcs->late_register(plane); 1641dedbd3bSFrançois Tigeot if (ret) 1651dedbd3bSFrançois Tigeot return ret; 1661dedbd3bSFrançois Tigeot } 1671dedbd3bSFrançois Tigeot 1681dedbd3bSFrançois Tigeot return 0; 1691dedbd3bSFrançois Tigeot } 1701dedbd3bSFrançois Tigeot 1711dedbd3bSFrançois Tigeot void drm_plane_unregister_all(struct drm_device *dev) 1721dedbd3bSFrançois Tigeot { 1731dedbd3bSFrançois Tigeot struct drm_plane *plane; 1741dedbd3bSFrançois Tigeot 1751dedbd3bSFrançois Tigeot drm_for_each_plane(plane, dev) { 1761dedbd3bSFrançois Tigeot if (plane->funcs->early_unregister) 1771dedbd3bSFrançois Tigeot plane->funcs->early_unregister(plane); 1781dedbd3bSFrançois Tigeot } 1791dedbd3bSFrançois Tigeot } 1801dedbd3bSFrançois Tigeot 1811dedbd3bSFrançois Tigeot /** 1821dedbd3bSFrançois Tigeot * drm_plane_init - Initialize a legacy plane 1831dedbd3bSFrançois Tigeot * @dev: DRM device 1841dedbd3bSFrançois Tigeot * @plane: plane object to init 1851dedbd3bSFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 1861dedbd3bSFrançois Tigeot * @funcs: callbacks for the new plane 1871dedbd3bSFrançois Tigeot * @formats: array of supported formats (DRM_FORMAT\_\*) 1881dedbd3bSFrançois Tigeot * @format_count: number of elements in @formats 1891dedbd3bSFrançois Tigeot * @is_primary: plane type (primary vs overlay) 1901dedbd3bSFrançois Tigeot * 1911dedbd3bSFrançois Tigeot * Legacy API to initialize a DRM plane. 1921dedbd3bSFrançois Tigeot * 1931dedbd3bSFrançois Tigeot * New drivers should call drm_universal_plane_init() instead. 1941dedbd3bSFrançois Tigeot * 1951dedbd3bSFrançois Tigeot * Returns: 1961dedbd3bSFrançois Tigeot * Zero on success, error code on failure. 1971dedbd3bSFrançois Tigeot */ 1981dedbd3bSFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 199*4be47400SFrançois Tigeot uint32_t possible_crtcs, 2001dedbd3bSFrançois Tigeot const struct drm_plane_funcs *funcs, 2011dedbd3bSFrançois Tigeot const uint32_t *formats, unsigned int format_count, 2021dedbd3bSFrançois Tigeot bool is_primary) 2031dedbd3bSFrançois Tigeot { 2041dedbd3bSFrançois Tigeot enum drm_plane_type type; 2051dedbd3bSFrançois Tigeot 2061dedbd3bSFrançois Tigeot type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; 2071dedbd3bSFrançois Tigeot return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, 2081dedbd3bSFrançois Tigeot formats, format_count, type, NULL); 2091dedbd3bSFrançois Tigeot } 2101dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_plane_init); 2111dedbd3bSFrançois Tigeot 2121dedbd3bSFrançois Tigeot /** 2131dedbd3bSFrançois Tigeot * drm_plane_cleanup - Clean up the core plane usage 2141dedbd3bSFrançois Tigeot * @plane: plane to cleanup 2151dedbd3bSFrançois Tigeot * 2161dedbd3bSFrançois Tigeot * This function cleans up @plane and removes it from the DRM mode setting 2171dedbd3bSFrançois Tigeot * core. Note that the function does *not* free the plane structure itself, 2181dedbd3bSFrançois Tigeot * this is the responsibility of the caller. 2191dedbd3bSFrançois Tigeot */ 2201dedbd3bSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane) 2211dedbd3bSFrançois Tigeot { 2221dedbd3bSFrançois Tigeot struct drm_device *dev = plane->dev; 2231dedbd3bSFrançois Tigeot 224*4be47400SFrançois Tigeot drm_modeset_lock_fini(&plane->mutex); 225*4be47400SFrançois Tigeot 2261dedbd3bSFrançois Tigeot kfree(plane->format_types); 2271dedbd3bSFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 2281dedbd3bSFrançois Tigeot 2291dedbd3bSFrançois Tigeot BUG_ON(list_empty(&plane->head)); 2301dedbd3bSFrançois Tigeot 2311dedbd3bSFrançois Tigeot /* Note that the plane_list is considered to be static; should we 2321dedbd3bSFrançois Tigeot * remove the drm_plane at runtime we would have to decrement all 2331dedbd3bSFrançois Tigeot * the indices on the drm_plane after us in the plane_list. 2341dedbd3bSFrançois Tigeot */ 2351dedbd3bSFrançois Tigeot 2361dedbd3bSFrançois Tigeot list_del(&plane->head); 2371dedbd3bSFrançois Tigeot dev->mode_config.num_total_plane--; 2381dedbd3bSFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 2391dedbd3bSFrançois Tigeot dev->mode_config.num_overlay_plane--; 2401dedbd3bSFrançois Tigeot 2411dedbd3bSFrançois Tigeot WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); 2421dedbd3bSFrançois Tigeot if (plane->state && plane->funcs->atomic_destroy_state) 2431dedbd3bSFrançois Tigeot plane->funcs->atomic_destroy_state(plane, plane->state); 2441dedbd3bSFrançois Tigeot 2451dedbd3bSFrançois Tigeot kfree(plane->name); 2461dedbd3bSFrançois Tigeot 2471dedbd3bSFrançois Tigeot memset(plane, 0, sizeof(*plane)); 2481dedbd3bSFrançois Tigeot } 2491dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup); 2501dedbd3bSFrançois Tigeot 2511dedbd3bSFrançois Tigeot /** 2521dedbd3bSFrançois Tigeot * drm_plane_from_index - find the registered plane at an index 2531dedbd3bSFrançois Tigeot * @dev: DRM device 2541dedbd3bSFrançois Tigeot * @idx: index of registered plane to find for 2551dedbd3bSFrançois Tigeot * 2561dedbd3bSFrançois Tigeot * Given a plane index, return the registered plane from DRM device's 2571dedbd3bSFrançois Tigeot * list of planes with matching index. 2581dedbd3bSFrançois Tigeot */ 2591dedbd3bSFrançois Tigeot struct drm_plane * 2601dedbd3bSFrançois Tigeot drm_plane_from_index(struct drm_device *dev, int idx) 2611dedbd3bSFrançois Tigeot { 2621dedbd3bSFrançois Tigeot struct drm_plane *plane; 2631dedbd3bSFrançois Tigeot 2641dedbd3bSFrançois Tigeot drm_for_each_plane(plane, dev) 2651dedbd3bSFrançois Tigeot if (idx == plane->index) 2661dedbd3bSFrançois Tigeot return plane; 2671dedbd3bSFrançois Tigeot 2681dedbd3bSFrançois Tigeot return NULL; 2691dedbd3bSFrançois Tigeot } 2701dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_plane_from_index); 2711dedbd3bSFrançois Tigeot 2721dedbd3bSFrançois Tigeot /** 2731dedbd3bSFrançois Tigeot * drm_plane_force_disable - Forcibly disable a plane 2741dedbd3bSFrançois Tigeot * @plane: plane to disable 2751dedbd3bSFrançois Tigeot * 2761dedbd3bSFrançois Tigeot * Forces the plane to be disabled. 2771dedbd3bSFrançois Tigeot * 2781dedbd3bSFrançois Tigeot * Used when the plane's current framebuffer is destroyed, 2791dedbd3bSFrançois Tigeot * and when restoring fbdev mode. 2801dedbd3bSFrançois Tigeot */ 2811dedbd3bSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane) 2821dedbd3bSFrançois Tigeot { 2831dedbd3bSFrançois Tigeot int ret; 2841dedbd3bSFrançois Tigeot 2851dedbd3bSFrançois Tigeot if (!plane->fb) 2861dedbd3bSFrançois Tigeot return; 2871dedbd3bSFrançois Tigeot 2881dedbd3bSFrançois Tigeot plane->old_fb = plane->fb; 2891dedbd3bSFrançois Tigeot ret = plane->funcs->disable_plane(plane); 2901dedbd3bSFrançois Tigeot if (ret) { 2911dedbd3bSFrançois Tigeot DRM_ERROR("failed to disable plane with busy fb\n"); 2921dedbd3bSFrançois Tigeot plane->old_fb = NULL; 2931dedbd3bSFrançois Tigeot return; 2941dedbd3bSFrançois Tigeot } 2951dedbd3bSFrançois Tigeot /* disconnect the plane from the fb and crtc: */ 2961dedbd3bSFrançois Tigeot drm_framebuffer_unreference(plane->old_fb); 2971dedbd3bSFrançois Tigeot plane->old_fb = NULL; 2981dedbd3bSFrançois Tigeot plane->fb = NULL; 2991dedbd3bSFrançois Tigeot plane->crtc = NULL; 3001dedbd3bSFrançois Tigeot } 3011dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable); 3021dedbd3bSFrançois Tigeot 3031dedbd3bSFrançois Tigeot /** 3041dedbd3bSFrançois Tigeot * drm_mode_plane_set_obj_prop - set the value of a property 3051dedbd3bSFrançois Tigeot * @plane: drm plane object to set property value for 3061dedbd3bSFrançois Tigeot * @property: property to set 3071dedbd3bSFrançois Tigeot * @value: value the property should be set to 3081dedbd3bSFrançois Tigeot * 3091dedbd3bSFrançois Tigeot * This functions sets a given property on a given plane object. This function 3101dedbd3bSFrançois Tigeot * calls the driver's ->set_property callback and changes the software state of 3111dedbd3bSFrançois Tigeot * the property if the callback succeeds. 3121dedbd3bSFrançois Tigeot * 3131dedbd3bSFrançois Tigeot * Returns: 3141dedbd3bSFrançois Tigeot * Zero on success, error code on failure. 3151dedbd3bSFrançois Tigeot */ 3161dedbd3bSFrançois Tigeot int drm_mode_plane_set_obj_prop(struct drm_plane *plane, 3171dedbd3bSFrançois Tigeot struct drm_property *property, 3181dedbd3bSFrançois Tigeot uint64_t value) 3191dedbd3bSFrançois Tigeot { 3201dedbd3bSFrançois Tigeot int ret = -EINVAL; 3211dedbd3bSFrançois Tigeot struct drm_mode_object *obj = &plane->base; 3221dedbd3bSFrançois Tigeot 3231dedbd3bSFrançois Tigeot if (plane->funcs->set_property) 3241dedbd3bSFrançois Tigeot ret = plane->funcs->set_property(plane, property, value); 3251dedbd3bSFrançois Tigeot if (!ret) 3261dedbd3bSFrançois Tigeot drm_object_property_set_value(obj, property, value); 3271dedbd3bSFrançois Tigeot 3281dedbd3bSFrançois Tigeot return ret; 3291dedbd3bSFrançois Tigeot } 3301dedbd3bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); 3311dedbd3bSFrançois Tigeot 3321dedbd3bSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data, 3331dedbd3bSFrançois Tigeot struct drm_file *file_priv) 3341dedbd3bSFrançois Tigeot { 3351dedbd3bSFrançois Tigeot struct drm_mode_get_plane_res *plane_resp = data; 3361dedbd3bSFrançois Tigeot struct drm_mode_config *config; 3371dedbd3bSFrançois Tigeot struct drm_plane *plane; 3381dedbd3bSFrançois Tigeot uint32_t __user *plane_ptr; 3391dedbd3bSFrançois Tigeot int copied = 0; 3401dedbd3bSFrançois Tigeot unsigned num_planes; 3411dedbd3bSFrançois Tigeot 3421dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3431dedbd3bSFrançois Tigeot return -EINVAL; 3441dedbd3bSFrançois Tigeot 3451dedbd3bSFrançois Tigeot config = &dev->mode_config; 3461dedbd3bSFrançois Tigeot 3471dedbd3bSFrançois Tigeot if (file_priv->universal_planes) 3481dedbd3bSFrançois Tigeot num_planes = config->num_total_plane; 3491dedbd3bSFrançois Tigeot else 3501dedbd3bSFrançois Tigeot num_planes = config->num_overlay_plane; 3511dedbd3bSFrançois Tigeot 3521dedbd3bSFrançois Tigeot /* 3531dedbd3bSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 3541dedbd3bSFrançois Tigeot * needed, and the 2nd time to fill it. 3551dedbd3bSFrançois Tigeot */ 3561dedbd3bSFrançois Tigeot if (num_planes && 3571dedbd3bSFrançois Tigeot (plane_resp->count_planes >= num_planes)) { 3581dedbd3bSFrançois Tigeot plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; 3591dedbd3bSFrançois Tigeot 3601dedbd3bSFrançois Tigeot /* Plane lists are invariant, no locking needed. */ 3611dedbd3bSFrançois Tigeot drm_for_each_plane(plane, dev) { 3621dedbd3bSFrançois Tigeot /* 3631dedbd3bSFrançois Tigeot * Unless userspace set the 'universal planes' 3641dedbd3bSFrançois Tigeot * capability bit, only advertise overlays. 3651dedbd3bSFrançois Tigeot */ 3661dedbd3bSFrançois Tigeot if (plane->type != DRM_PLANE_TYPE_OVERLAY && 3671dedbd3bSFrançois Tigeot !file_priv->universal_planes) 3681dedbd3bSFrançois Tigeot continue; 3691dedbd3bSFrançois Tigeot 3701dedbd3bSFrançois Tigeot if (put_user(plane->base.id, plane_ptr + copied)) 3711dedbd3bSFrançois Tigeot return -EFAULT; 3721dedbd3bSFrançois Tigeot copied++; 3731dedbd3bSFrançois Tigeot } 3741dedbd3bSFrançois Tigeot } 3751dedbd3bSFrançois Tigeot plane_resp->count_planes = num_planes; 3761dedbd3bSFrançois Tigeot 3771dedbd3bSFrançois Tigeot return 0; 3781dedbd3bSFrançois Tigeot } 3791dedbd3bSFrançois Tigeot 3801dedbd3bSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data, 3811dedbd3bSFrançois Tigeot struct drm_file *file_priv) 3821dedbd3bSFrançois Tigeot { 3831dedbd3bSFrançois Tigeot struct drm_mode_get_plane *plane_resp = data; 3841dedbd3bSFrançois Tigeot struct drm_plane *plane; 3851dedbd3bSFrançois Tigeot uint32_t __user *format_ptr; 3861dedbd3bSFrançois Tigeot 3871dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3881dedbd3bSFrançois Tigeot return -EINVAL; 3891dedbd3bSFrançois Tigeot 3901dedbd3bSFrançois Tigeot plane = drm_plane_find(dev, plane_resp->plane_id); 3911dedbd3bSFrançois Tigeot if (!plane) 3921dedbd3bSFrançois Tigeot return -ENOENT; 3931dedbd3bSFrançois Tigeot 3941dedbd3bSFrançois Tigeot drm_modeset_lock(&plane->mutex, NULL); 3951dedbd3bSFrançois Tigeot if (plane->crtc) 3961dedbd3bSFrançois Tigeot plane_resp->crtc_id = plane->crtc->base.id; 3971dedbd3bSFrançois Tigeot else 3981dedbd3bSFrançois Tigeot plane_resp->crtc_id = 0; 3991dedbd3bSFrançois Tigeot 4001dedbd3bSFrançois Tigeot if (plane->fb) 4011dedbd3bSFrançois Tigeot plane_resp->fb_id = plane->fb->base.id; 4021dedbd3bSFrançois Tigeot else 4031dedbd3bSFrançois Tigeot plane_resp->fb_id = 0; 4041dedbd3bSFrançois Tigeot drm_modeset_unlock(&plane->mutex); 4051dedbd3bSFrançois Tigeot 4061dedbd3bSFrançois Tigeot plane_resp->plane_id = plane->base.id; 4071dedbd3bSFrançois Tigeot plane_resp->possible_crtcs = plane->possible_crtcs; 4081dedbd3bSFrançois Tigeot plane_resp->gamma_size = 0; 4091dedbd3bSFrançois Tigeot 4101dedbd3bSFrançois Tigeot /* 4111dedbd3bSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 4121dedbd3bSFrançois Tigeot * needed, and the 2nd time to fill it. 4131dedbd3bSFrançois Tigeot */ 4141dedbd3bSFrançois Tigeot if (plane->format_count && 4151dedbd3bSFrançois Tigeot (plane_resp->count_format_types >= plane->format_count)) { 4161dedbd3bSFrançois Tigeot format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; 4171dedbd3bSFrançois Tigeot if (copy_to_user(format_ptr, 4181dedbd3bSFrançois Tigeot plane->format_types, 4191dedbd3bSFrançois Tigeot sizeof(uint32_t) * plane->format_count)) { 4201dedbd3bSFrançois Tigeot return -EFAULT; 4211dedbd3bSFrançois Tigeot } 4221dedbd3bSFrançois Tigeot } 4231dedbd3bSFrançois Tigeot plane_resp->count_format_types = plane->format_count; 4241dedbd3bSFrançois Tigeot 4251dedbd3bSFrançois Tigeot return 0; 4261dedbd3bSFrançois Tigeot } 4271dedbd3bSFrançois Tigeot 4281dedbd3bSFrançois Tigeot int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) 4291dedbd3bSFrançois Tigeot { 4301dedbd3bSFrançois Tigeot unsigned int i; 4311dedbd3bSFrançois Tigeot 4321dedbd3bSFrançois Tigeot for (i = 0; i < plane->format_count; i++) { 4331dedbd3bSFrançois Tigeot if (format == plane->format_types[i]) 4341dedbd3bSFrançois Tigeot return 0; 4351dedbd3bSFrançois Tigeot } 4361dedbd3bSFrançois Tigeot 4371dedbd3bSFrançois Tigeot return -EINVAL; 4381dedbd3bSFrançois Tigeot } 4391dedbd3bSFrançois Tigeot 4401dedbd3bSFrançois Tigeot /* 4411dedbd3bSFrançois Tigeot * setplane_internal - setplane handler for internal callers 4421dedbd3bSFrançois Tigeot * 4431dedbd3bSFrançois Tigeot * Note that we assume an extra reference has already been taken on fb. If the 4441dedbd3bSFrançois Tigeot * update fails, this reference will be dropped before return; if it succeeds, 4451dedbd3bSFrançois Tigeot * the previous framebuffer (if any) will be unreferenced instead. 4461dedbd3bSFrançois Tigeot * 4471dedbd3bSFrançois Tigeot * src_{x,y,w,h} are provided in 16.16 fixed point format 4481dedbd3bSFrançois Tigeot */ 4491dedbd3bSFrançois Tigeot static int __setplane_internal(struct drm_plane *plane, 4501dedbd3bSFrançois Tigeot struct drm_crtc *crtc, 4511dedbd3bSFrançois Tigeot struct drm_framebuffer *fb, 4521dedbd3bSFrançois Tigeot int32_t crtc_x, int32_t crtc_y, 4531dedbd3bSFrançois Tigeot uint32_t crtc_w, uint32_t crtc_h, 4541dedbd3bSFrançois Tigeot /* src_{x,y,w,h} values are 16.16 fixed point */ 4551dedbd3bSFrançois Tigeot uint32_t src_x, uint32_t src_y, 4561dedbd3bSFrançois Tigeot uint32_t src_w, uint32_t src_h) 4571dedbd3bSFrançois Tigeot { 4581dedbd3bSFrançois Tigeot int ret = 0; 4591dedbd3bSFrançois Tigeot 4601dedbd3bSFrançois Tigeot /* No fb means shut it down */ 4611dedbd3bSFrançois Tigeot if (!fb) { 4621dedbd3bSFrançois Tigeot plane->old_fb = plane->fb; 4631dedbd3bSFrançois Tigeot ret = plane->funcs->disable_plane(plane); 4641dedbd3bSFrançois Tigeot if (!ret) { 4651dedbd3bSFrançois Tigeot plane->crtc = NULL; 4661dedbd3bSFrançois Tigeot plane->fb = NULL; 4671dedbd3bSFrançois Tigeot } else { 4681dedbd3bSFrançois Tigeot plane->old_fb = NULL; 4691dedbd3bSFrançois Tigeot } 4701dedbd3bSFrançois Tigeot goto out; 4711dedbd3bSFrançois Tigeot } 4721dedbd3bSFrançois Tigeot 4731dedbd3bSFrançois Tigeot /* Check whether this plane is usable on this CRTC */ 4741dedbd3bSFrançois Tigeot if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { 4751dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Invalid crtc for plane\n"); 4761dedbd3bSFrançois Tigeot ret = -EINVAL; 4771dedbd3bSFrançois Tigeot goto out; 4781dedbd3bSFrançois Tigeot } 4791dedbd3bSFrançois Tigeot 4801dedbd3bSFrançois Tigeot /* Check whether this plane supports the fb pixel format. */ 4811dedbd3bSFrançois Tigeot ret = drm_plane_check_pixel_format(plane, fb->pixel_format); 4821dedbd3bSFrançois Tigeot if (ret) { 483*4be47400SFrançois Tigeot struct drm_format_name_buf format_name; 484*4be47400SFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n", 485*4be47400SFrançois Tigeot drm_get_format_name(fb->pixel_format, 486*4be47400SFrançois Tigeot &format_name)); 4871dedbd3bSFrançois Tigeot goto out; 4881dedbd3bSFrançois Tigeot } 4891dedbd3bSFrançois Tigeot 4901dedbd3bSFrançois Tigeot /* Give drivers some help against integer overflows */ 4911dedbd3bSFrançois Tigeot if (crtc_w > INT_MAX || 4921dedbd3bSFrançois Tigeot crtc_x > INT_MAX - (int32_t) crtc_w || 4931dedbd3bSFrançois Tigeot crtc_h > INT_MAX || 4941dedbd3bSFrançois Tigeot crtc_y > INT_MAX - (int32_t) crtc_h) { 4951dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 4961dedbd3bSFrançois Tigeot crtc_w, crtc_h, crtc_x, crtc_y); 4971dedbd3bSFrançois Tigeot ret = -ERANGE; 4981dedbd3bSFrançois Tigeot goto out; 4991dedbd3bSFrançois Tigeot } 5001dedbd3bSFrançois Tigeot 5011dedbd3bSFrançois Tigeot ret = drm_framebuffer_check_src_coords(src_x, src_y, src_w, src_h, fb); 5021dedbd3bSFrançois Tigeot if (ret) 5031dedbd3bSFrançois Tigeot goto out; 5041dedbd3bSFrançois Tigeot 5051dedbd3bSFrançois Tigeot plane->old_fb = plane->fb; 5061dedbd3bSFrançois Tigeot ret = plane->funcs->update_plane(plane, crtc, fb, 5071dedbd3bSFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 5081dedbd3bSFrançois Tigeot src_x, src_y, src_w, src_h); 5091dedbd3bSFrançois Tigeot if (!ret) { 5101dedbd3bSFrançois Tigeot plane->crtc = crtc; 5111dedbd3bSFrançois Tigeot plane->fb = fb; 5121dedbd3bSFrançois Tigeot fb = NULL; 5131dedbd3bSFrançois Tigeot } else { 5141dedbd3bSFrançois Tigeot plane->old_fb = NULL; 5151dedbd3bSFrançois Tigeot } 5161dedbd3bSFrançois Tigeot 5171dedbd3bSFrançois Tigeot out: 5181dedbd3bSFrançois Tigeot if (fb) 5191dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 5201dedbd3bSFrançois Tigeot if (plane->old_fb) 5211dedbd3bSFrançois Tigeot drm_framebuffer_unreference(plane->old_fb); 5221dedbd3bSFrançois Tigeot plane->old_fb = NULL; 5231dedbd3bSFrançois Tigeot 5241dedbd3bSFrançois Tigeot return ret; 5251dedbd3bSFrançois Tigeot } 5261dedbd3bSFrançois Tigeot 5271dedbd3bSFrançois Tigeot static int setplane_internal(struct drm_plane *plane, 5281dedbd3bSFrançois Tigeot struct drm_crtc *crtc, 5291dedbd3bSFrançois Tigeot struct drm_framebuffer *fb, 5301dedbd3bSFrançois Tigeot int32_t crtc_x, int32_t crtc_y, 5311dedbd3bSFrançois Tigeot uint32_t crtc_w, uint32_t crtc_h, 5321dedbd3bSFrançois Tigeot /* src_{x,y,w,h} values are 16.16 fixed point */ 5331dedbd3bSFrançois Tigeot uint32_t src_x, uint32_t src_y, 5341dedbd3bSFrançois Tigeot uint32_t src_w, uint32_t src_h) 5351dedbd3bSFrançois Tigeot { 5361dedbd3bSFrançois Tigeot int ret; 5371dedbd3bSFrançois Tigeot 5381dedbd3bSFrançois Tigeot drm_modeset_lock_all(plane->dev); 5391dedbd3bSFrançois Tigeot ret = __setplane_internal(plane, crtc, fb, 5401dedbd3bSFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 5411dedbd3bSFrançois Tigeot src_x, src_y, src_w, src_h); 5421dedbd3bSFrançois Tigeot drm_modeset_unlock_all(plane->dev); 5431dedbd3bSFrançois Tigeot 5441dedbd3bSFrançois Tigeot return ret; 5451dedbd3bSFrançois Tigeot } 5461dedbd3bSFrançois Tigeot 5471dedbd3bSFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data, 5481dedbd3bSFrançois Tigeot struct drm_file *file_priv) 5491dedbd3bSFrançois Tigeot { 5501dedbd3bSFrançois Tigeot struct drm_mode_set_plane *plane_req = data; 5511dedbd3bSFrançois Tigeot struct drm_plane *plane; 5521dedbd3bSFrançois Tigeot struct drm_crtc *crtc = NULL; 5531dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL; 5541dedbd3bSFrançois Tigeot 5551dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 5561dedbd3bSFrançois Tigeot return -EINVAL; 5571dedbd3bSFrançois Tigeot 5581dedbd3bSFrançois Tigeot /* 5591dedbd3bSFrançois Tigeot * First, find the plane, crtc, and fb objects. If not available, 5601dedbd3bSFrançois Tigeot * we don't bother to call the driver. 5611dedbd3bSFrançois Tigeot */ 5621dedbd3bSFrançois Tigeot plane = drm_plane_find(dev, plane_req->plane_id); 5631dedbd3bSFrançois Tigeot if (!plane) { 5641dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Unknown plane ID %d\n", 5651dedbd3bSFrançois Tigeot plane_req->plane_id); 5661dedbd3bSFrançois Tigeot return -ENOENT; 5671dedbd3bSFrançois Tigeot } 5681dedbd3bSFrançois Tigeot 5691dedbd3bSFrançois Tigeot if (plane_req->fb_id) { 5701dedbd3bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, plane_req->fb_id); 5711dedbd3bSFrançois Tigeot if (!fb) { 5721dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 5731dedbd3bSFrançois Tigeot plane_req->fb_id); 5741dedbd3bSFrançois Tigeot return -ENOENT; 5751dedbd3bSFrançois Tigeot } 5761dedbd3bSFrançois Tigeot 5771dedbd3bSFrançois Tigeot crtc = drm_crtc_find(dev, plane_req->crtc_id); 5781dedbd3bSFrançois Tigeot if (!crtc) { 5791dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Unknown crtc ID %d\n", 5801dedbd3bSFrançois Tigeot plane_req->crtc_id); 5811dedbd3bSFrançois Tigeot return -ENOENT; 5821dedbd3bSFrançois Tigeot } 5831dedbd3bSFrançois Tigeot } 5841dedbd3bSFrançois Tigeot 5851dedbd3bSFrançois Tigeot /* 5861dedbd3bSFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 5871dedbd3bSFrançois Tigeot * framebuffer depending on success. 5881dedbd3bSFrançois Tigeot */ 5891dedbd3bSFrançois Tigeot return setplane_internal(plane, crtc, fb, 5901dedbd3bSFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y, 5911dedbd3bSFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 5921dedbd3bSFrançois Tigeot plane_req->src_x, plane_req->src_y, 5931dedbd3bSFrançois Tigeot plane_req->src_w, plane_req->src_h); 5941dedbd3bSFrançois Tigeot } 5951dedbd3bSFrançois Tigeot 5961dedbd3bSFrançois Tigeot static int drm_mode_cursor_universal(struct drm_crtc *crtc, 5971dedbd3bSFrançois Tigeot struct drm_mode_cursor2 *req, 5981dedbd3bSFrançois Tigeot struct drm_file *file_priv) 5991dedbd3bSFrançois Tigeot { 6001dedbd3bSFrançois Tigeot struct drm_device *dev = crtc->dev; 6011dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL; 6021dedbd3bSFrançois Tigeot struct drm_mode_fb_cmd2 fbreq = { 6031dedbd3bSFrançois Tigeot .width = req->width, 6041dedbd3bSFrançois Tigeot .height = req->height, 6051dedbd3bSFrançois Tigeot .pixel_format = DRM_FORMAT_ARGB8888, 6061dedbd3bSFrançois Tigeot .pitches = { req->width * 4 }, 6071dedbd3bSFrançois Tigeot .handles = { req->handle }, 6081dedbd3bSFrançois Tigeot }; 6091dedbd3bSFrançois Tigeot int32_t crtc_x, crtc_y; 6101dedbd3bSFrançois Tigeot uint32_t crtc_w = 0, crtc_h = 0; 6111dedbd3bSFrançois Tigeot uint32_t src_w = 0, src_h = 0; 6121dedbd3bSFrançois Tigeot int ret = 0; 6131dedbd3bSFrançois Tigeot 6141dedbd3bSFrançois Tigeot BUG_ON(!crtc->cursor); 6151dedbd3bSFrançois Tigeot WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); 6161dedbd3bSFrançois Tigeot 6171dedbd3bSFrançois Tigeot /* 6181dedbd3bSFrançois Tigeot * Obtain fb we'll be using (either new or existing) and take an extra 6191dedbd3bSFrançois Tigeot * reference to it if fb != null. setplane will take care of dropping 6201dedbd3bSFrançois Tigeot * the reference if the plane update fails. 6211dedbd3bSFrançois Tigeot */ 6221dedbd3bSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 6231dedbd3bSFrançois Tigeot if (req->handle) { 6241dedbd3bSFrançois Tigeot fb = drm_internal_framebuffer_create(dev, &fbreq, file_priv); 6251dedbd3bSFrançois Tigeot if (IS_ERR(fb)) { 6261dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); 6271dedbd3bSFrançois Tigeot return PTR_ERR(fb); 6281dedbd3bSFrançois Tigeot } 6291dedbd3bSFrançois Tigeot fb->hot_x = req->hot_x; 6301dedbd3bSFrançois Tigeot fb->hot_y = req->hot_y; 6311dedbd3bSFrançois Tigeot } else { 6321dedbd3bSFrançois Tigeot fb = NULL; 6331dedbd3bSFrançois Tigeot } 6341dedbd3bSFrançois Tigeot } else { 6351dedbd3bSFrançois Tigeot fb = crtc->cursor->fb; 6361dedbd3bSFrançois Tigeot if (fb) 6371dedbd3bSFrançois Tigeot drm_framebuffer_reference(fb); 6381dedbd3bSFrançois Tigeot } 6391dedbd3bSFrançois Tigeot 6401dedbd3bSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 6411dedbd3bSFrançois Tigeot crtc_x = req->x; 6421dedbd3bSFrançois Tigeot crtc_y = req->y; 6431dedbd3bSFrançois Tigeot } else { 6441dedbd3bSFrançois Tigeot crtc_x = crtc->cursor_x; 6451dedbd3bSFrançois Tigeot crtc_y = crtc->cursor_y; 6461dedbd3bSFrançois Tigeot } 6471dedbd3bSFrançois Tigeot 6481dedbd3bSFrançois Tigeot if (fb) { 6491dedbd3bSFrançois Tigeot crtc_w = fb->width; 6501dedbd3bSFrançois Tigeot crtc_h = fb->height; 6511dedbd3bSFrançois Tigeot src_w = fb->width << 16; 6521dedbd3bSFrançois Tigeot src_h = fb->height << 16; 6531dedbd3bSFrançois Tigeot } 6541dedbd3bSFrançois Tigeot 6551dedbd3bSFrançois Tigeot /* 6561dedbd3bSFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 6571dedbd3bSFrançois Tigeot * framebuffer depending on success. 6581dedbd3bSFrançois Tigeot */ 6591dedbd3bSFrançois Tigeot ret = __setplane_internal(crtc->cursor, crtc, fb, 6601dedbd3bSFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 6611dedbd3bSFrançois Tigeot 0, 0, src_w, src_h); 6621dedbd3bSFrançois Tigeot 6631dedbd3bSFrançois Tigeot /* Update successful; save new cursor position, if necessary */ 6641dedbd3bSFrançois Tigeot if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { 6651dedbd3bSFrançois Tigeot crtc->cursor_x = req->x; 6661dedbd3bSFrançois Tigeot crtc->cursor_y = req->y; 6671dedbd3bSFrançois Tigeot } 6681dedbd3bSFrançois Tigeot 6691dedbd3bSFrançois Tigeot return ret; 6701dedbd3bSFrançois Tigeot } 6711dedbd3bSFrançois Tigeot 6721dedbd3bSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev, 6731dedbd3bSFrançois Tigeot struct drm_mode_cursor2 *req, 6741dedbd3bSFrançois Tigeot struct drm_file *file_priv) 6751dedbd3bSFrançois Tigeot { 6761dedbd3bSFrançois Tigeot struct drm_crtc *crtc; 6771dedbd3bSFrançois Tigeot int ret = 0; 6781dedbd3bSFrançois Tigeot 6791dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 6801dedbd3bSFrançois Tigeot return -EINVAL; 6811dedbd3bSFrançois Tigeot 6821dedbd3bSFrançois Tigeot if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) 6831dedbd3bSFrançois Tigeot return -EINVAL; 6841dedbd3bSFrançois Tigeot 6851dedbd3bSFrançois Tigeot crtc = drm_crtc_find(dev, req->crtc_id); 6861dedbd3bSFrançois Tigeot if (!crtc) { 6871dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 6881dedbd3bSFrançois Tigeot return -ENOENT; 6891dedbd3bSFrançois Tigeot } 6901dedbd3bSFrançois Tigeot 6911dedbd3bSFrançois Tigeot /* 6921dedbd3bSFrançois Tigeot * If this crtc has a universal cursor plane, call that plane's update 6931dedbd3bSFrançois Tigeot * handler rather than using legacy cursor handlers. 6941dedbd3bSFrançois Tigeot */ 6951dedbd3bSFrançois Tigeot drm_modeset_lock_crtc(crtc, crtc->cursor); 6961dedbd3bSFrançois Tigeot if (crtc->cursor) { 6971dedbd3bSFrançois Tigeot ret = drm_mode_cursor_universal(crtc, req, file_priv); 6981dedbd3bSFrançois Tigeot goto out; 6991dedbd3bSFrançois Tigeot } 7001dedbd3bSFrançois Tigeot 7011dedbd3bSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 7021dedbd3bSFrançois Tigeot if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { 7031dedbd3bSFrançois Tigeot ret = -ENXIO; 7041dedbd3bSFrançois Tigeot goto out; 7051dedbd3bSFrançois Tigeot } 7061dedbd3bSFrançois Tigeot /* Turns off the cursor if handle is 0 */ 7071dedbd3bSFrançois Tigeot if (crtc->funcs->cursor_set2) 7081dedbd3bSFrançois Tigeot ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, 7091dedbd3bSFrançois Tigeot req->width, req->height, req->hot_x, req->hot_y); 7101dedbd3bSFrançois Tigeot else 7111dedbd3bSFrançois Tigeot ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, 7121dedbd3bSFrançois Tigeot req->width, req->height); 7131dedbd3bSFrançois Tigeot } 7141dedbd3bSFrançois Tigeot 7151dedbd3bSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 7161dedbd3bSFrançois Tigeot if (crtc->funcs->cursor_move) { 7171dedbd3bSFrançois Tigeot ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 7181dedbd3bSFrançois Tigeot } else { 7191dedbd3bSFrançois Tigeot ret = -EFAULT; 7201dedbd3bSFrançois Tigeot goto out; 7211dedbd3bSFrançois Tigeot } 7221dedbd3bSFrançois Tigeot } 7231dedbd3bSFrançois Tigeot out: 7241dedbd3bSFrançois Tigeot drm_modeset_unlock_crtc(crtc); 7251dedbd3bSFrançois Tigeot 7261dedbd3bSFrançois Tigeot return ret; 7271dedbd3bSFrançois Tigeot 7281dedbd3bSFrançois Tigeot } 7291dedbd3bSFrançois Tigeot 7301dedbd3bSFrançois Tigeot 7311dedbd3bSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev, 7321dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 7331dedbd3bSFrançois Tigeot { 7341dedbd3bSFrançois Tigeot struct drm_mode_cursor *req = data; 7351dedbd3bSFrançois Tigeot struct drm_mode_cursor2 new_req; 7361dedbd3bSFrançois Tigeot 7371dedbd3bSFrançois Tigeot memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); 7381dedbd3bSFrançois Tigeot new_req.hot_x = new_req.hot_y = 0; 7391dedbd3bSFrançois Tigeot 7401dedbd3bSFrançois Tigeot return drm_mode_cursor_common(dev, &new_req, file_priv); 7411dedbd3bSFrançois Tigeot } 7421dedbd3bSFrançois Tigeot 7431dedbd3bSFrançois Tigeot /* 7441dedbd3bSFrançois Tigeot * Set the cursor configuration based on user request. This implements the 2nd 7451dedbd3bSFrançois Tigeot * version of the cursor ioctl, which allows userspace to additionally specify 7461dedbd3bSFrançois Tigeot * the hotspot of the pointer. 7471dedbd3bSFrançois Tigeot */ 7481dedbd3bSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev, 7491dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 7501dedbd3bSFrançois Tigeot { 7511dedbd3bSFrançois Tigeot struct drm_mode_cursor2 *req = data; 7521dedbd3bSFrançois Tigeot 7531dedbd3bSFrançois Tigeot return drm_mode_cursor_common(dev, req, file_priv); 7541dedbd3bSFrançois Tigeot } 7551dedbd3bSFrançois Tigeot 7561dedbd3bSFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev, 7571dedbd3bSFrançois Tigeot void *data, struct drm_file *file_priv) 7581dedbd3bSFrançois Tigeot { 7591dedbd3bSFrançois Tigeot struct drm_mode_crtc_page_flip_target *page_flip = data; 7601dedbd3bSFrançois Tigeot struct drm_crtc *crtc; 7611dedbd3bSFrançois Tigeot struct drm_framebuffer *fb = NULL; 7621dedbd3bSFrançois Tigeot struct drm_pending_vblank_event *e = NULL; 7631dedbd3bSFrançois Tigeot u32 target_vblank = page_flip->sequence; 7641dedbd3bSFrançois Tigeot int ret = -EINVAL; 7651dedbd3bSFrançois Tigeot 7661dedbd3bSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 7671dedbd3bSFrançois Tigeot return -EINVAL; 7681dedbd3bSFrançois Tigeot 7691dedbd3bSFrançois Tigeot if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS) 7701dedbd3bSFrançois Tigeot return -EINVAL; 7711dedbd3bSFrançois Tigeot 7721dedbd3bSFrançois Tigeot if (page_flip->sequence != 0 && !(page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) 7731dedbd3bSFrançois Tigeot return -EINVAL; 7741dedbd3bSFrançois Tigeot 7751dedbd3bSFrançois Tigeot /* Only one of the DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE/RELATIVE flags 7761dedbd3bSFrançois Tigeot * can be specified 7771dedbd3bSFrançois Tigeot */ 7781dedbd3bSFrançois Tigeot if ((page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) == DRM_MODE_PAGE_FLIP_TARGET) 7791dedbd3bSFrançois Tigeot return -EINVAL; 7801dedbd3bSFrançois Tigeot 7811dedbd3bSFrançois Tigeot if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) 7821dedbd3bSFrançois Tigeot return -EINVAL; 7831dedbd3bSFrançois Tigeot 7841dedbd3bSFrançois Tigeot crtc = drm_crtc_find(dev, page_flip->crtc_id); 7851dedbd3bSFrançois Tigeot if (!crtc) 7861dedbd3bSFrançois Tigeot return -ENOENT; 7871dedbd3bSFrançois Tigeot 7881dedbd3bSFrançois Tigeot if (crtc->funcs->page_flip_target) { 7891dedbd3bSFrançois Tigeot u32 current_vblank; 7901dedbd3bSFrançois Tigeot int r; 7911dedbd3bSFrançois Tigeot 7921dedbd3bSFrançois Tigeot r = drm_crtc_vblank_get(crtc); 7931dedbd3bSFrançois Tigeot if (r) 7941dedbd3bSFrançois Tigeot return r; 7951dedbd3bSFrançois Tigeot 7961dedbd3bSFrançois Tigeot current_vblank = drm_crtc_vblank_count(crtc); 7971dedbd3bSFrançois Tigeot 7981dedbd3bSFrançois Tigeot switch (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET) { 7991dedbd3bSFrançois Tigeot case DRM_MODE_PAGE_FLIP_TARGET_ABSOLUTE: 8001dedbd3bSFrançois Tigeot if ((int)(target_vblank - current_vblank) > 1) { 8011dedbd3bSFrançois Tigeot DRM_DEBUG("Invalid absolute flip target %u, " 8021dedbd3bSFrançois Tigeot "must be <= %u\n", target_vblank, 8031dedbd3bSFrançois Tigeot current_vblank + 1); 8041dedbd3bSFrançois Tigeot drm_crtc_vblank_put(crtc); 8051dedbd3bSFrançois Tigeot return -EINVAL; 8061dedbd3bSFrançois Tigeot } 8071dedbd3bSFrançois Tigeot break; 8081dedbd3bSFrançois Tigeot case DRM_MODE_PAGE_FLIP_TARGET_RELATIVE: 8091dedbd3bSFrançois Tigeot if (target_vblank != 0 && target_vblank != 1) { 8101dedbd3bSFrançois Tigeot DRM_DEBUG("Invalid relative flip target %u, " 8111dedbd3bSFrançois Tigeot "must be 0 or 1\n", target_vblank); 8121dedbd3bSFrançois Tigeot drm_crtc_vblank_put(crtc); 8131dedbd3bSFrançois Tigeot return -EINVAL; 8141dedbd3bSFrançois Tigeot } 8151dedbd3bSFrançois Tigeot target_vblank += current_vblank; 8161dedbd3bSFrançois Tigeot break; 8171dedbd3bSFrançois Tigeot default: 8181dedbd3bSFrançois Tigeot target_vblank = current_vblank + 8191dedbd3bSFrançois Tigeot !(page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC); 8201dedbd3bSFrançois Tigeot break; 8211dedbd3bSFrançois Tigeot } 8221dedbd3bSFrançois Tigeot } else if (crtc->funcs->page_flip == NULL || 8231dedbd3bSFrançois Tigeot (page_flip->flags & DRM_MODE_PAGE_FLIP_TARGET)) { 8241dedbd3bSFrançois Tigeot return -EINVAL; 8251dedbd3bSFrançois Tigeot } 8261dedbd3bSFrançois Tigeot 8271dedbd3bSFrançois Tigeot drm_modeset_lock_crtc(crtc, crtc->primary); 8281dedbd3bSFrançois Tigeot if (crtc->primary->fb == NULL) { 8291dedbd3bSFrançois Tigeot /* The framebuffer is currently unbound, presumably 8301dedbd3bSFrançois Tigeot * due to a hotplug event, that userspace has not 8311dedbd3bSFrançois Tigeot * yet discovered. 8321dedbd3bSFrançois Tigeot */ 8331dedbd3bSFrançois Tigeot ret = -EBUSY; 8341dedbd3bSFrançois Tigeot goto out; 8351dedbd3bSFrançois Tigeot } 8361dedbd3bSFrançois Tigeot 8371dedbd3bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, page_flip->fb_id); 8381dedbd3bSFrançois Tigeot if (!fb) { 8391dedbd3bSFrançois Tigeot ret = -ENOENT; 8401dedbd3bSFrançois Tigeot goto out; 8411dedbd3bSFrançois Tigeot } 8421dedbd3bSFrançois Tigeot 8431dedbd3bSFrançois Tigeot if (crtc->state) { 8441dedbd3bSFrançois Tigeot const struct drm_plane_state *state = crtc->primary->state; 8451dedbd3bSFrançois Tigeot 8461dedbd3bSFrançois Tigeot ret = drm_framebuffer_check_src_coords(state->src_x, 8471dedbd3bSFrançois Tigeot state->src_y, 8481dedbd3bSFrançois Tigeot state->src_w, 8491dedbd3bSFrançois Tigeot state->src_h, 8501dedbd3bSFrançois Tigeot fb); 8511dedbd3bSFrançois Tigeot } else { 8521dedbd3bSFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); 8531dedbd3bSFrançois Tigeot } 8541dedbd3bSFrançois Tigeot if (ret) 8551dedbd3bSFrançois Tigeot goto out; 8561dedbd3bSFrançois Tigeot 8571dedbd3bSFrançois Tigeot if (crtc->primary->fb->pixel_format != fb->pixel_format) { 8581dedbd3bSFrançois Tigeot DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); 8591dedbd3bSFrançois Tigeot ret = -EINVAL; 8601dedbd3bSFrançois Tigeot goto out; 8611dedbd3bSFrançois Tigeot } 8621dedbd3bSFrançois Tigeot 8631dedbd3bSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 8641dedbd3bSFrançois Tigeot e = kzalloc(sizeof *e, GFP_KERNEL); 8651dedbd3bSFrançois Tigeot if (!e) { 8661dedbd3bSFrançois Tigeot ret = -ENOMEM; 8671dedbd3bSFrançois Tigeot goto out; 8681dedbd3bSFrançois Tigeot } 8691dedbd3bSFrançois Tigeot e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 8701dedbd3bSFrançois Tigeot e->event.base.length = sizeof(e->event); 8711dedbd3bSFrançois Tigeot e->event.user_data = page_flip->user_data; 8721dedbd3bSFrançois Tigeot ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); 8731dedbd3bSFrançois Tigeot if (ret) { 8741dedbd3bSFrançois Tigeot kfree(e); 8751dedbd3bSFrançois Tigeot goto out; 8761dedbd3bSFrançois Tigeot } 8771dedbd3bSFrançois Tigeot } 8781dedbd3bSFrançois Tigeot 8791dedbd3bSFrançois Tigeot crtc->primary->old_fb = crtc->primary->fb; 8801dedbd3bSFrançois Tigeot if (crtc->funcs->page_flip_target) 8811dedbd3bSFrançois Tigeot ret = crtc->funcs->page_flip_target(crtc, fb, e, 8821dedbd3bSFrançois Tigeot page_flip->flags, 8831dedbd3bSFrançois Tigeot target_vblank); 8841dedbd3bSFrançois Tigeot else 8851dedbd3bSFrançois Tigeot ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); 8861dedbd3bSFrançois Tigeot if (ret) { 8871dedbd3bSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) 8881dedbd3bSFrançois Tigeot drm_event_cancel_free(dev, &e->base); 8891dedbd3bSFrançois Tigeot /* Keep the old fb, don't unref it. */ 8901dedbd3bSFrançois Tigeot crtc->primary->old_fb = NULL; 8911dedbd3bSFrançois Tigeot } else { 8921dedbd3bSFrançois Tigeot crtc->primary->fb = fb; 8931dedbd3bSFrançois Tigeot /* Unref only the old framebuffer. */ 8941dedbd3bSFrançois Tigeot fb = NULL; 8951dedbd3bSFrançois Tigeot } 8961dedbd3bSFrançois Tigeot 8971dedbd3bSFrançois Tigeot out: 8981dedbd3bSFrançois Tigeot if (ret && crtc->funcs->page_flip_target) 8991dedbd3bSFrançois Tigeot drm_crtc_vblank_put(crtc); 9001dedbd3bSFrançois Tigeot if (fb) 9011dedbd3bSFrançois Tigeot drm_framebuffer_unreference(fb); 9021dedbd3bSFrançois Tigeot if (crtc->primary->old_fb) 9031dedbd3bSFrançois Tigeot drm_framebuffer_unreference(crtc->primary->old_fb); 9041dedbd3bSFrançois Tigeot crtc->primary->old_fb = NULL; 9051dedbd3bSFrançois Tigeot drm_modeset_unlock_crtc(crtc); 9061dedbd3bSFrançois Tigeot 9071dedbd3bSFrançois Tigeot return ret; 9081dedbd3bSFrançois Tigeot } 909