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> 34b5162e19SFrançois Tigeot #include <linux/export.h> 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 38b5162e19SFrançois Tigeot #include <uapi_drm/drm_fourcc.h> 39ba55f2f5SFrançois Tigeot #include <linux/slab.h> 40ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h> 41ba55f2f5SFrançois Tigeot 42ba55f2f5SFrançois Tigeot #include "drm_crtc_internal.h" 435718399fSFrançois Tigeot 44*24edb884SFrançois Tigeot static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, 45*24edb884SFrançois Tigeot struct drm_mode_fb_cmd2 *r, 46*24edb884SFrançois Tigeot struct drm_file *file_priv); 47*24edb884SFrançois Tigeot 48a2fdbec6SFrançois Tigeot /** 49a2fdbec6SFrançois Tigeot * drm_modeset_lock_all - take all modeset locks 50a2fdbec6SFrançois Tigeot * @dev: drm device 51a2fdbec6SFrançois Tigeot * 52a2fdbec6SFrançois Tigeot * This function takes all modeset locks, suitable where a more fine-grained 53ba55f2f5SFrançois Tigeot * scheme isn't (yet) implemented. Locks must be dropped with 54ba55f2f5SFrançois Tigeot * drm_modeset_unlock_all. 55a2fdbec6SFrançois Tigeot */ 56a2fdbec6SFrançois Tigeot void drm_modeset_lock_all(struct drm_device *dev) 57a2fdbec6SFrançois Tigeot { 58ba55f2f5SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 59ba55f2f5SFrançois Tigeot struct drm_modeset_acquire_ctx *ctx; 60ba55f2f5SFrançois Tigeot int ret; 61a2fdbec6SFrançois Tigeot 62ba55f2f5SFrançois Tigeot ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); 63ba55f2f5SFrançois Tigeot if (WARN_ON(!ctx)) 64ba55f2f5SFrançois Tigeot return; 65a2fdbec6SFrançois Tigeot 66ba55f2f5SFrançois Tigeot mutex_lock(&config->mutex); 67ba55f2f5SFrançois Tigeot 68ba55f2f5SFrançois Tigeot drm_modeset_acquire_init(ctx, 0); 69ba55f2f5SFrançois Tigeot 70ba55f2f5SFrançois Tigeot retry: 71ba55f2f5SFrançois Tigeot ret = drm_modeset_lock(&config->connection_mutex, ctx); 72ba55f2f5SFrançois Tigeot if (ret) 73ba55f2f5SFrançois Tigeot goto fail; 74ba55f2f5SFrançois Tigeot ret = drm_modeset_lock_all_crtcs(dev, ctx); 75ba55f2f5SFrançois Tigeot if (ret) 76ba55f2f5SFrançois Tigeot goto fail; 77ba55f2f5SFrançois Tigeot 78ba55f2f5SFrançois Tigeot WARN_ON(config->acquire_ctx); 79ba55f2f5SFrançois Tigeot 80ba55f2f5SFrançois Tigeot /* now we hold the locks, so now that it is safe, stash the 81ba55f2f5SFrançois Tigeot * ctx for drm_modeset_unlock_all(): 82ba55f2f5SFrançois Tigeot */ 83ba55f2f5SFrançois Tigeot config->acquire_ctx = ctx; 84ba55f2f5SFrançois Tigeot 85ba55f2f5SFrançois Tigeot drm_warn_on_modeset_not_all_locked(dev); 86ba55f2f5SFrançois Tigeot 87ba55f2f5SFrançois Tigeot return; 88ba55f2f5SFrançois Tigeot 89ba55f2f5SFrançois Tigeot fail: 90ba55f2f5SFrançois Tigeot if (ret == -EDEADLK) { 91ba55f2f5SFrançois Tigeot drm_modeset_backoff(ctx); 92ba55f2f5SFrançois Tigeot goto retry; 93ba55f2f5SFrançois Tigeot } 94a2fdbec6SFrançois Tigeot } 95a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all); 96a2fdbec6SFrançois Tigeot 97a2fdbec6SFrançois Tigeot /** 98a2fdbec6SFrançois Tigeot * drm_modeset_unlock_all - drop all modeset locks 99a2fdbec6SFrançois Tigeot * @dev: device 100ba55f2f5SFrançois Tigeot * 101ba55f2f5SFrançois Tigeot * This function drop all modeset locks taken by drm_modeset_lock_all. 102a2fdbec6SFrançois Tigeot */ 103a2fdbec6SFrançois Tigeot void drm_modeset_unlock_all(struct drm_device *dev) 104a2fdbec6SFrançois Tigeot { 105ba55f2f5SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 106ba55f2f5SFrançois Tigeot struct drm_modeset_acquire_ctx *ctx = config->acquire_ctx; 107a2fdbec6SFrançois Tigeot 108ba55f2f5SFrançois Tigeot if (WARN_ON(!ctx)) 109ba55f2f5SFrançois Tigeot return; 110ba55f2f5SFrançois Tigeot 111ba55f2f5SFrançois Tigeot config->acquire_ctx = NULL; 112ba55f2f5SFrançois Tigeot drm_modeset_drop_locks(ctx); 113ba55f2f5SFrançois Tigeot drm_modeset_acquire_fini(ctx); 114ba55f2f5SFrançois Tigeot 115ba55f2f5SFrançois Tigeot kfree(ctx); 116a2fdbec6SFrançois Tigeot 1174dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 118a2fdbec6SFrançois Tigeot } 119a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock_all); 120a2fdbec6SFrançois Tigeot 1214dbb207bSFrançois Tigeot /** 1224dbb207bSFrançois Tigeot * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked 1234dbb207bSFrançois Tigeot * @dev: device 124ba55f2f5SFrançois Tigeot * 125ba55f2f5SFrançois Tigeot * Useful as a debug assert. 1264dbb207bSFrançois Tigeot */ 1274dbb207bSFrançois Tigeot void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) 1284dbb207bSFrançois Tigeot { 1294dbb207bSFrançois Tigeot struct drm_crtc *crtc; 1304dbb207bSFrançois Tigeot 1314dbb207bSFrançois Tigeot /* Locking is currently fubar in the panic handler. */ 1329edbd4a0SFrançois Tigeot #if 0 1334dbb207bSFrançois Tigeot if (oops_in_progress) 1344dbb207bSFrançois Tigeot return; 1354dbb207bSFrançois Tigeot #endif 1364dbb207bSFrançois Tigeot 1374dbb207bSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 138ba55f2f5SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&crtc->mutex)); 1394dbb207bSFrançois Tigeot 140ba55f2f5SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&dev->mode_config.connection_mutex)); 1414dbb207bSFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 1424dbb207bSFrançois Tigeot } 1434dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); 1444dbb207bSFrançois Tigeot 1455718399fSFrançois Tigeot /* Avoid boilerplate. I'm tired of typing. */ 1465718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list) \ 14706fede5aSFrançois Tigeot const char *fnname(int val) \ 1485718399fSFrançois Tigeot { \ 1495718399fSFrançois Tigeot int i; \ 150b5162e19SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(list); i++) { \ 1515718399fSFrançois Tigeot if (list[i].type == val) \ 1525718399fSFrançois Tigeot return list[i].name; \ 1535718399fSFrançois Tigeot } \ 1545718399fSFrançois Tigeot return "(unknown)"; \ 1555718399fSFrançois Tigeot } 1565718399fSFrançois Tigeot 1575718399fSFrançois Tigeot /* 1585718399fSFrançois Tigeot * Global properties 1595718399fSFrançois Tigeot */ 16006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] = 1615718399fSFrançois Tigeot { { DRM_MODE_DPMS_ON, "On" }, 1625718399fSFrançois Tigeot { DRM_MODE_DPMS_STANDBY, "Standby" }, 1635718399fSFrançois Tigeot { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 1645718399fSFrançois Tigeot { DRM_MODE_DPMS_OFF, "Off" } 1655718399fSFrançois Tigeot }; 1665718399fSFrançois Tigeot 1675718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 1685718399fSFrançois Tigeot 169ba55f2f5SFrançois Tigeot static const struct drm_prop_enum_list drm_plane_type_enum_list[] = 170ba55f2f5SFrançois Tigeot { 171ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, 172ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_PRIMARY, "Primary" }, 173ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_CURSOR, "Cursor" }, 174ba55f2f5SFrançois Tigeot }; 175ba55f2f5SFrançois Tigeot 1765718399fSFrançois Tigeot /* 1775718399fSFrançois Tigeot * Optional properties 1785718399fSFrançois Tigeot */ 17906fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = 1805718399fSFrançois Tigeot { 1815718399fSFrançois Tigeot { DRM_MODE_SCALE_NONE, "None" }, 1825718399fSFrançois Tigeot { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 1835718399fSFrançois Tigeot { DRM_MODE_SCALE_CENTER, "Center" }, 1845718399fSFrançois Tigeot { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 1855718399fSFrançois Tigeot }; 1865718399fSFrançois Tigeot 187*24edb884SFrançois Tigeot static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { 188*24edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, 189*24edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, 190*24edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, 191*24edb884SFrançois Tigeot }; 192*24edb884SFrançois Tigeot 1935718399fSFrançois Tigeot /* 1945718399fSFrançois Tigeot * Non-global properties, but "required" for certain connectors. 1955718399fSFrançois Tigeot */ 19606fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = 1975718399fSFrançois Tigeot { 1985718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 1995718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 2005718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 2015718399fSFrançois Tigeot }; 2025718399fSFrançois Tigeot 2035718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 2045718399fSFrançois Tigeot 20506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = 2065718399fSFrançois Tigeot { 2075718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 2085718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 2095718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 2105718399fSFrançois Tigeot }; 2115718399fSFrançois Tigeot 2125718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 2135718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list) 2145718399fSFrançois Tigeot 21506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] = 2165718399fSFrançois Tigeot { 2175718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 2185718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 2195718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 2205718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 2215718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 2225718399fSFrançois Tigeot }; 2235718399fSFrançois Tigeot 2245718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 2255718399fSFrançois Tigeot 22606fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = 2275718399fSFrançois Tigeot { 2285718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 2295718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 2305718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 2315718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 2325718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 2335718399fSFrançois Tigeot }; 2345718399fSFrançois Tigeot 2355718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 2365718399fSFrançois Tigeot drm_tv_subconnector_enum_list) 2375718399fSFrançois Tigeot 23806fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { 2395718399fSFrançois Tigeot { DRM_MODE_DIRTY_OFF, "Off" }, 2405718399fSFrançois Tigeot { DRM_MODE_DIRTY_ON, "On" }, 2415718399fSFrançois Tigeot { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, 2425718399fSFrançois Tigeot }; 2435718399fSFrançois Tigeot 2445718399fSFrançois Tigeot struct drm_conn_prop_enum_list { 2455718399fSFrançois Tigeot int type; 24606fede5aSFrançois Tigeot const char *name; 2475718399fSFrançois Tigeot int count; 2485718399fSFrançois Tigeot }; 2495718399fSFrançois Tigeot 2505718399fSFrançois Tigeot /* 2515718399fSFrançois Tigeot * Connector and encoder types. 2525718399fSFrançois Tigeot */ 2535718399fSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] = 2549edbd4a0SFrançois Tigeot { { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, 2559edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VGA, "VGA" }, 2569edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 2579edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 2589edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 2599edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Composite, "Composite" }, 2609edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, 2619edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 2629edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Component, "Component" }, 2639edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, 2649edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, 2659edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 2669edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 2679edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_TV, "TV" }, 2689edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_eDP, "eDP" }, 2699edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, 2709edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DSI, "DSI" }, 2715718399fSFrançois Tigeot }; 2725718399fSFrançois Tigeot 27306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] = 2745718399fSFrançois Tigeot { { DRM_MODE_ENCODER_NONE, "None" }, 2755718399fSFrançois Tigeot { DRM_MODE_ENCODER_DAC, "DAC" }, 2765718399fSFrançois Tigeot { DRM_MODE_ENCODER_TMDS, "TMDS" }, 2775718399fSFrançois Tigeot { DRM_MODE_ENCODER_LVDS, "LVDS" }, 2785718399fSFrançois Tigeot { DRM_MODE_ENCODER_TVDAC, "TV" }, 279b5162e19SFrançois Tigeot { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, 2809edbd4a0SFrançois Tigeot { DRM_MODE_ENCODER_DSI, "DSI" }, 281ba55f2f5SFrançois Tigeot { DRM_MODE_ENCODER_DPMST, "DP MST" }, 2825718399fSFrançois Tigeot }; 2835718399fSFrançois Tigeot 284ba55f2f5SFrançois Tigeot static const struct drm_prop_enum_list drm_subpixel_enum_list[] = 2855718399fSFrançois Tigeot { 286ba55f2f5SFrançois Tigeot { SubPixelUnknown, "Unknown" }, 287ba55f2f5SFrançois Tigeot { SubPixelHorizontalRGB, "Horizontal RGB" }, 288ba55f2f5SFrançois Tigeot { SubPixelHorizontalBGR, "Horizontal BGR" }, 289ba55f2f5SFrançois Tigeot { SubPixelVerticalRGB, "Vertical RGB" }, 290ba55f2f5SFrançois Tigeot { SubPixelVerticalBGR, "Vertical BGR" }, 291ba55f2f5SFrançois Tigeot { SubPixelNone, "None" }, 292ba55f2f5SFrançois Tigeot }; 2935718399fSFrançois Tigeot 294ba55f2f5SFrançois Tigeot void drm_connector_ida_init(void) 2955718399fSFrançois Tigeot { 296ba55f2f5SFrançois Tigeot #if 0 297ba55f2f5SFrançois Tigeot int i; 2985718399fSFrançois Tigeot 299ba55f2f5SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 300ba55f2f5SFrançois Tigeot ida_init(&drm_connector_enum_list[i].ida); 301ba55f2f5SFrançois Tigeot #endif 3025718399fSFrançois Tigeot } 3035718399fSFrançois Tigeot 304ba55f2f5SFrançois Tigeot void drm_connector_ida_destroy(void) 305ba55f2f5SFrançois Tigeot { 306ba55f2f5SFrançois Tigeot #if 0 307ba55f2f5SFrançois Tigeot int i; 308ba55f2f5SFrançois Tigeot 309ba55f2f5SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 310ba55f2f5SFrançois Tigeot ida_destroy(&drm_connector_enum_list[i].ida); 311ba55f2f5SFrançois Tigeot #endif 312ba55f2f5SFrançois Tigeot } 313ba55f2f5SFrançois Tigeot 314ba55f2f5SFrançois Tigeot /** 315ba55f2f5SFrançois Tigeot * drm_get_connector_status_name - return a string for connector status 316ba55f2f5SFrançois Tigeot * @status: connector status to compute name of 317ba55f2f5SFrançois Tigeot * 318ba55f2f5SFrançois Tigeot * In contrast to the other drm_get_*_name functions this one here returns a 319ba55f2f5SFrançois Tigeot * const pointer and hence is threadsafe. 320ba55f2f5SFrançois Tigeot */ 32106fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status) 3225718399fSFrançois Tigeot { 3235718399fSFrançois Tigeot if (status == connector_status_connected) 3245718399fSFrançois Tigeot return "connected"; 3255718399fSFrançois Tigeot else if (status == connector_status_disconnected) 3265718399fSFrançois Tigeot return "disconnected"; 3275718399fSFrançois Tigeot else 3285718399fSFrançois Tigeot return "unknown"; 3295718399fSFrançois Tigeot } 3304dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name); 3315718399fSFrançois Tigeot 332ba55f2f5SFrançois Tigeot /** 333ba55f2f5SFrançois Tigeot * drm_get_subpixel_order_name - return a string for a given subpixel enum 334ba55f2f5SFrançois Tigeot * @order: enum of subpixel_order 335ba55f2f5SFrançois Tigeot * 336ba55f2f5SFrançois Tigeot * Note you could abuse this and return something out of bounds, but that 337ba55f2f5SFrançois Tigeot * would be a caller error. No unscrubbed user data should make it here. 338ba55f2f5SFrançois Tigeot */ 339ba55f2f5SFrançois Tigeot const char *drm_get_subpixel_order_name(enum subpixel_order order) 340ba55f2f5SFrançois Tigeot { 341ba55f2f5SFrançois Tigeot return drm_subpixel_enum_list[order].name; 342ba55f2f5SFrançois Tigeot } 343ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_subpixel_order_name); 344ba55f2f5SFrançois Tigeot 34506fede5aSFrançois Tigeot static char printable_char(int c) 34606fede5aSFrançois Tigeot { 34706fede5aSFrançois Tigeot return isascii(c) && isprint(c) ? c : '?'; 34806fede5aSFrançois Tigeot } 34906fede5aSFrançois Tigeot 350ba55f2f5SFrançois Tigeot /** 351ba55f2f5SFrançois Tigeot * drm_get_format_name - return a string for drm fourcc format 352ba55f2f5SFrançois Tigeot * @format: format to compute name of 353ba55f2f5SFrançois Tigeot * 354ba55f2f5SFrançois Tigeot * Note that the buffer used by this function is globally shared and owned by 355ba55f2f5SFrançois Tigeot * the function itself. 356ba55f2f5SFrançois Tigeot * 357ba55f2f5SFrançois Tigeot * FIXME: This isn't really multithreading safe. 358ba55f2f5SFrançois Tigeot */ 35906fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format) 36006fede5aSFrançois Tigeot { 36106fede5aSFrançois Tigeot static char buf[32]; 36206fede5aSFrançois Tigeot 36306fede5aSFrançois Tigeot ksnprintf(buf, sizeof(buf), 36406fede5aSFrançois Tigeot "%c%c%c%c %s-endian (0x%08x)", 36506fede5aSFrançois Tigeot printable_char(format & 0xff), 36606fede5aSFrançois Tigeot printable_char((format >> 8) & 0xff), 36706fede5aSFrançois Tigeot printable_char((format >> 16) & 0xff), 36806fede5aSFrançois Tigeot printable_char((format >> 24) & 0x7f), 36906fede5aSFrançois Tigeot format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", 37006fede5aSFrançois Tigeot format); 37106fede5aSFrançois Tigeot 37206fede5aSFrançois Tigeot return buf; 37306fede5aSFrançois Tigeot } 37406fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name); 37506fede5aSFrançois Tigeot 3765718399fSFrançois Tigeot /** 3774dbb207bSFrançois Tigeot * drm_mode_object_get - allocate a new modeset identifier 3785718399fSFrançois Tigeot * @dev: DRM device 3794dbb207bSFrançois Tigeot * @obj: object pointer, used to generate unique ID 3804dbb207bSFrançois Tigeot * @obj_type: object type 3815718399fSFrançois Tigeot * 3825718399fSFrançois Tigeot * Create a unique identifier based on @ptr in @dev's identifier space. Used 383ba55f2f5SFrançois Tigeot * for tracking modes, CRTCs and connectors. Note that despite the _get postfix 384ba55f2f5SFrançois Tigeot * modeset identifiers are _not_ reference counted. Hence don't use this for 385ba55f2f5SFrançois Tigeot * reference counted modeset objects like framebuffers. 3865718399fSFrançois Tigeot * 387ba55f2f5SFrançois Tigeot * Returns: 3885718399fSFrançois Tigeot * New unique (relative to other objects in @dev) integer identifier for the 3895718399fSFrançois Tigeot * object. 3905718399fSFrançois Tigeot */ 391ba55f2f5SFrançois Tigeot int drm_mode_object_get(struct drm_device *dev, 3925718399fSFrançois Tigeot struct drm_mode_object *obj, uint32_t obj_type) 3935718399fSFrançois Tigeot { 3945718399fSFrançois Tigeot int ret; 3955718399fSFrançois Tigeot 3969f0f5970SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 3979f0f5970SFrançois Tigeot ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); 3989f0f5970SFrançois Tigeot if (ret >= 0) { 3994dbb207bSFrançois Tigeot /* 4004dbb207bSFrançois Tigeot * Set up the object linking under the protection of the idr 4014dbb207bSFrançois Tigeot * lock so that other users can't see inconsistent state. 4024dbb207bSFrançois Tigeot */ 4039f0f5970SFrançois Tigeot obj->id = ret; 4045718399fSFrançois Tigeot obj->type = obj_type; 4059f0f5970SFrançois Tigeot } 4069f0f5970SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 4079f0f5970SFrançois Tigeot 4089f0f5970SFrançois Tigeot return ret < 0 ? ret : 0; 4095718399fSFrançois Tigeot } 4105718399fSFrançois Tigeot 4115718399fSFrançois Tigeot /** 4124dbb207bSFrançois Tigeot * drm_mode_object_put - free a modeset identifer 4135718399fSFrançois Tigeot * @dev: DRM device 4144dbb207bSFrançois Tigeot * @object: object to free 4155718399fSFrançois Tigeot * 416ba55f2f5SFrançois Tigeot * Free @id from @dev's unique identifier pool. Note that despite the _get 417ba55f2f5SFrançois Tigeot * postfix modeset identifiers are _not_ reference counted. Hence don't use this 418ba55f2f5SFrançois Tigeot * for reference counted modeset objects like framebuffers. 4195718399fSFrançois Tigeot */ 420ba55f2f5SFrançois Tigeot void drm_mode_object_put(struct drm_device *dev, 4215718399fSFrançois Tigeot struct drm_mode_object *object) 4225718399fSFrançois Tigeot { 4234dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 424aea8bdbdSFrançois Tigeot idr_remove(&dev->mode_config.crtc_idr, object->id); 4254dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 4265718399fSFrançois Tigeot } 4275718399fSFrançois Tigeot 428ba55f2f5SFrançois Tigeot static struct drm_mode_object *_object_find(struct drm_device *dev, 429ba55f2f5SFrançois Tigeot uint32_t id, uint32_t type) 430ba55f2f5SFrançois Tigeot { 431ba55f2f5SFrançois Tigeot struct drm_mode_object *obj = NULL; 432ba55f2f5SFrançois Tigeot 433ba55f2f5SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 434ba55f2f5SFrançois Tigeot obj = idr_find(&dev->mode_config.crtc_idr, id); 435*24edb884SFrançois Tigeot if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) 436*24edb884SFrançois Tigeot obj = NULL; 437*24edb884SFrançois Tigeot if (obj && obj->id != id) 438*24edb884SFrançois Tigeot obj = NULL; 439*24edb884SFrançois Tigeot /* don't leak out unref'd fb's */ 440*24edb884SFrançois Tigeot if (obj && (obj->type == DRM_MODE_OBJECT_FB)) 441ba55f2f5SFrançois Tigeot obj = NULL; 442ba55f2f5SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 443ba55f2f5SFrançois Tigeot 444ba55f2f5SFrançois Tigeot return obj; 445ba55f2f5SFrançois Tigeot } 446ba55f2f5SFrançois Tigeot 4474dbb207bSFrançois Tigeot /** 4484dbb207bSFrançois Tigeot * drm_mode_object_find - look up a drm object with static lifetime 4494dbb207bSFrançois Tigeot * @dev: drm device 4504dbb207bSFrançois Tigeot * @id: id of the mode object 4514dbb207bSFrançois Tigeot * @type: type of the mode object 4524dbb207bSFrançois Tigeot * 4534dbb207bSFrançois Tigeot * Note that framebuffers cannot be looked up with this functions - since those 454ba55f2f5SFrançois Tigeot * are reference counted, they need special treatment. Even with 455ba55f2f5SFrançois Tigeot * DRM_MODE_OBJECT_ANY (although that will simply return NULL 456ba55f2f5SFrançois Tigeot * rather than WARN_ON()). 4574dbb207bSFrançois Tigeot */ 4585718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, 4595718399fSFrançois Tigeot uint32_t id, uint32_t type) 4605718399fSFrançois Tigeot { 461aea8bdbdSFrançois Tigeot struct drm_mode_object *obj = NULL; 4625718399fSFrançois Tigeot 4634dbb207bSFrançois Tigeot /* Framebuffers are reference counted and need their own lookup 4644dbb207bSFrançois Tigeot * function.*/ 4654dbb207bSFrançois Tigeot WARN_ON(type == DRM_MODE_OBJECT_FB); 466ba55f2f5SFrançois Tigeot obj = _object_find(dev, id, type); 4675718399fSFrançois Tigeot return obj; 4685718399fSFrançois Tigeot } 469b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find); 4705718399fSFrançois Tigeot 4715718399fSFrançois Tigeot /** 4725718399fSFrançois Tigeot * drm_framebuffer_init - initialize a framebuffer 4735718399fSFrançois Tigeot * @dev: DRM device 4744dbb207bSFrançois Tigeot * @fb: framebuffer to be initialized 4754dbb207bSFrançois Tigeot * @funcs: ... with these functions 4765718399fSFrançois Tigeot * 4775718399fSFrançois Tigeot * Allocates an ID for the framebuffer's parent mode object, sets its mode 4785718399fSFrançois Tigeot * functions & device file and adds it to the master fd list. 4795718399fSFrançois Tigeot * 4804dbb207bSFrançois Tigeot * IMPORTANT: 4814dbb207bSFrançois Tigeot * This functions publishes the fb and makes it available for concurrent access 4824dbb207bSFrançois Tigeot * by other users. Which means by this point the fb _must_ be fully set up - 4834dbb207bSFrançois Tigeot * since all the fb attributes are invariant over its lifetime, no further 4844dbb207bSFrançois Tigeot * locking but only correct reference counting is required. 4854dbb207bSFrançois Tigeot * 486ba55f2f5SFrançois Tigeot * Returns: 4875718399fSFrançois Tigeot * Zero on success, error code on failure. 4885718399fSFrançois Tigeot */ 4895718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 4905718399fSFrançois Tigeot const struct drm_framebuffer_funcs *funcs) 4915718399fSFrançois Tigeot { 4925718399fSFrançois Tigeot int ret; 4935718399fSFrançois Tigeot 4944dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 495b5162e19SFrançois Tigeot kref_init(&fb->refcount); 4964dbb207bSFrançois Tigeot INIT_LIST_HEAD(&fb->filp_head); 4974dbb207bSFrançois Tigeot fb->dev = dev; 4984dbb207bSFrançois Tigeot fb->funcs = funcs; 499b5162e19SFrançois Tigeot 5005718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 5015718399fSFrançois Tigeot if (ret) 5024dbb207bSFrançois Tigeot goto out; 5035718399fSFrançois Tigeot 5044dbb207bSFrançois Tigeot /* Grab the idr reference. */ 5054dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 5064dbb207bSFrançois Tigeot 5075718399fSFrançois Tigeot dev->mode_config.num_fb++; 5085718399fSFrançois Tigeot list_add(&fb->head, &dev->mode_config.fb_list); 5094dbb207bSFrançois Tigeot out: 5104dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 5115718399fSFrançois Tigeot 5125718399fSFrançois Tigeot return 0; 5135718399fSFrançois Tigeot } 514b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init); 515b5162e19SFrançois Tigeot 516b5162e19SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref) 517b5162e19SFrançois Tigeot { 518b5162e19SFrançois Tigeot struct drm_framebuffer *fb = 519b5162e19SFrançois Tigeot container_of(kref, struct drm_framebuffer, refcount); 520b5162e19SFrançois Tigeot fb->funcs->destroy(fb); 521b5162e19SFrançois Tigeot } 522b5162e19SFrançois Tigeot 5234dbb207bSFrançois Tigeot static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, 5244dbb207bSFrançois Tigeot uint32_t id) 5254dbb207bSFrançois Tigeot { 5264dbb207bSFrançois Tigeot struct drm_mode_object *obj = NULL; 5274dbb207bSFrançois Tigeot struct drm_framebuffer *fb; 5284dbb207bSFrançois Tigeot 5294dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 5304dbb207bSFrançois Tigeot obj = idr_find(&dev->mode_config.crtc_idr, id); 5314dbb207bSFrançois Tigeot if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) 5324dbb207bSFrançois Tigeot fb = NULL; 5334dbb207bSFrançois Tigeot else 5344dbb207bSFrançois Tigeot fb = obj_to_fb(obj); 5354dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 5364dbb207bSFrançois Tigeot 5374dbb207bSFrançois Tigeot return fb; 5384dbb207bSFrançois Tigeot } 5394dbb207bSFrançois Tigeot 5404dbb207bSFrançois Tigeot /** 5414dbb207bSFrançois Tigeot * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference 5424dbb207bSFrançois Tigeot * @dev: drm device 5434dbb207bSFrançois Tigeot * @id: id of the fb object 5444dbb207bSFrançois Tigeot * 5454dbb207bSFrançois Tigeot * If successful, this grabs an additional reference to the framebuffer - 5464dbb207bSFrançois Tigeot * callers need to make sure to eventually unreference the returned framebuffer 547ba55f2f5SFrançois Tigeot * again, using @drm_framebuffer_unreference. 5484dbb207bSFrançois Tigeot */ 5494dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, 5504dbb207bSFrançois Tigeot uint32_t id) 5514dbb207bSFrançois Tigeot { 5524dbb207bSFrançois Tigeot struct drm_framebuffer *fb; 5534dbb207bSFrançois Tigeot 5544dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 5554dbb207bSFrançois Tigeot fb = __drm_framebuffer_lookup(dev, id); 5564dbb207bSFrançois Tigeot if (fb) 5574dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 5584dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 5594dbb207bSFrançois Tigeot 5604dbb207bSFrançois Tigeot return fb; 5614dbb207bSFrançois Tigeot } 5624dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup); 5634dbb207bSFrançois Tigeot 564b5162e19SFrançois Tigeot /** 565b5162e19SFrançois Tigeot * drm_framebuffer_unreference - unref a framebuffer 5664dbb207bSFrançois Tigeot * @fb: framebuffer to unref 567b5162e19SFrançois Tigeot * 5684dbb207bSFrançois Tigeot * This functions decrements the fb's refcount and frees it if it drops to zero. 569b5162e19SFrançois Tigeot */ 570b5162e19SFrançois Tigeot void drm_framebuffer_unreference(struct drm_framebuffer *fb) 571b5162e19SFrançois Tigeot { 572ba55f2f5SFrançois Tigeot DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); 573b5162e19SFrançois Tigeot kref_put(&fb->refcount, drm_framebuffer_free); 574b5162e19SFrançois Tigeot } 575b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unreference); 576b5162e19SFrançois Tigeot 577b5162e19SFrançois Tigeot /** 578b5162e19SFrançois Tigeot * drm_framebuffer_reference - incr the fb refcnt 5794dbb207bSFrançois Tigeot * @fb: framebuffer 580ba55f2f5SFrançois Tigeot * 581ba55f2f5SFrançois Tigeot * This functions increments the fb's refcount. 582b5162e19SFrançois Tigeot */ 583b5162e19SFrançois Tigeot void drm_framebuffer_reference(struct drm_framebuffer *fb) 584b5162e19SFrançois Tigeot { 585ba55f2f5SFrançois Tigeot DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); 586b5162e19SFrançois Tigeot kref_get(&fb->refcount); 587b5162e19SFrançois Tigeot } 588b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_reference); 5895718399fSFrançois Tigeot 590a2fdbec6SFrançois Tigeot static void drm_framebuffer_free_bug(struct kref *kref) 591a2fdbec6SFrançois Tigeot { 592a2fdbec6SFrançois Tigeot BUG(); 593a2fdbec6SFrançois Tigeot } 594a2fdbec6SFrançois Tigeot 595a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) 596a2fdbec6SFrançois Tigeot { 597ba55f2f5SFrançois Tigeot DRM_DEBUG("%p: FB ID: %d (%d)\n", fb, fb->base.id, atomic_read(&fb->refcount.refcount)); 598a2fdbec6SFrançois Tigeot kref_put(&fb->refcount, drm_framebuffer_free_bug); 599a2fdbec6SFrançois Tigeot } 600a2fdbec6SFrançois Tigeot 601a2fdbec6SFrançois Tigeot /* dev->mode_config.fb_lock must be held! */ 602a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unregister(struct drm_device *dev, 603a2fdbec6SFrançois Tigeot struct drm_framebuffer *fb) 604a2fdbec6SFrançois Tigeot { 605a2fdbec6SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 606a2fdbec6SFrançois Tigeot idr_remove(&dev->mode_config.crtc_idr, fb->base.id); 607a2fdbec6SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 608a2fdbec6SFrançois Tigeot 609a2fdbec6SFrançois Tigeot fb->base.id = 0; 610a2fdbec6SFrançois Tigeot 611a2fdbec6SFrançois Tigeot __drm_framebuffer_unreference(fb); 612a2fdbec6SFrançois Tigeot } 613a2fdbec6SFrançois Tigeot 614a2fdbec6SFrançois Tigeot /** 615a2fdbec6SFrançois Tigeot * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 616a2fdbec6SFrançois Tigeot * @fb: fb to unregister 617a2fdbec6SFrançois Tigeot * 618a2fdbec6SFrançois Tigeot * Drivers need to call this when cleaning up driver-private framebuffers, e.g. 619a2fdbec6SFrançois Tigeot * those used for fbdev. Note that the caller must hold a reference of it's own, 620a2fdbec6SFrançois Tigeot * i.e. the object may not be destroyed through this call (since it'll lead to a 621a2fdbec6SFrançois Tigeot * locking inversion). 622a2fdbec6SFrançois Tigeot */ 623a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) 624a2fdbec6SFrançois Tigeot { 625a2fdbec6SFrançois Tigeot struct drm_device *dev = fb->dev; 626a2fdbec6SFrançois Tigeot 627a2fdbec6SFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 628a2fdbec6SFrançois Tigeot /* Mark fb as reaped and drop idr ref. */ 629a2fdbec6SFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 630a2fdbec6SFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 631a2fdbec6SFrançois Tigeot } 632a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private); 633a2fdbec6SFrançois Tigeot 6345718399fSFrançois Tigeot /** 6355718399fSFrançois Tigeot * drm_framebuffer_cleanup - remove a framebuffer object 6365718399fSFrançois Tigeot * @fb: framebuffer to remove 6375718399fSFrançois Tigeot * 638ba55f2f5SFrançois Tigeot * Cleanup framebuffer. This function is intended to be used from the drivers 639ba55f2f5SFrançois Tigeot * ->destroy callback. It can also be used to clean up driver private 640ba55f2f5SFrançois Tigeot * framebuffers embedded into a larger structure. 6415718399fSFrançois Tigeot * 6424dbb207bSFrançois Tigeot * Note that this function does not remove the fb from active usuage - if it is 6434dbb207bSFrançois Tigeot * still used anywhere, hilarity can ensue since userspace could call getfb on 6444dbb207bSFrançois Tigeot * the id and get back -EINVAL. Obviously no concern at driver unload time. 6454dbb207bSFrançois Tigeot * 6464dbb207bSFrançois Tigeot * Also, the framebuffer will not be removed from the lookup idr - for 6474dbb207bSFrançois Tigeot * user-created framebuffers this will happen in in the rmfb ioctl. For 6484dbb207bSFrançois Tigeot * driver-private objects (e.g. for fbdev) drivers need to explicitly call 6494dbb207bSFrançois Tigeot * drm_framebuffer_unregister_private. 6505718399fSFrançois Tigeot */ 6515718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 6525718399fSFrançois Tigeot { 6535718399fSFrançois Tigeot struct drm_device *dev = fb->dev; 6544dbb207bSFrançois Tigeot 6554dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 656b5162e19SFrançois Tigeot list_del(&fb->head); 657b5162e19SFrançois Tigeot dev->mode_config.num_fb--; 6584dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 659b5162e19SFrançois Tigeot } 660b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup); 661b5162e19SFrançois Tigeot 662b5162e19SFrançois Tigeot /** 663b5162e19SFrançois Tigeot * drm_framebuffer_remove - remove and unreference a framebuffer object 664b5162e19SFrançois Tigeot * @fb: framebuffer to remove 665b5162e19SFrançois Tigeot * 666b5162e19SFrançois Tigeot * Scans all the CRTCs and planes in @dev's mode_config. If they're 6674dbb207bSFrançois Tigeot * using @fb, removes it, setting it to NULL. Then drops the reference to the 6684dbb207bSFrançois Tigeot * passed-in framebuffer. Might take the modeset locks. 6694dbb207bSFrançois Tigeot * 6704dbb207bSFrançois Tigeot * Note that this function optimizes the cleanup away if the caller holds the 6714dbb207bSFrançois Tigeot * last reference to the framebuffer. It is also guaranteed to not take the 6724dbb207bSFrançois Tigeot * modeset locks in this case. 673b5162e19SFrançois Tigeot */ 674b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb) 675b5162e19SFrançois Tigeot { 676b5162e19SFrançois Tigeot struct drm_device *dev = fb->dev; 6775718399fSFrançois Tigeot struct drm_crtc *crtc; 6785718399fSFrançois Tigeot struct drm_plane *plane; 6795718399fSFrançois Tigeot struct drm_mode_set set; 6805718399fSFrançois Tigeot int ret; 6815718399fSFrançois Tigeot 6824dbb207bSFrançois Tigeot WARN_ON(!list_empty(&fb->filp_head)); 6834dbb207bSFrançois Tigeot 6844dbb207bSFrançois Tigeot /* 6854dbb207bSFrançois Tigeot * drm ABI mandates that we remove any deleted framebuffers from active 6864dbb207bSFrançois Tigeot * useage. But since most sane clients only remove framebuffers they no 6874dbb207bSFrançois Tigeot * longer need, try to optimize this away. 6884dbb207bSFrançois Tigeot * 6894dbb207bSFrançois Tigeot * Since we're holding a reference ourselves, observing a refcount of 1 6904dbb207bSFrançois Tigeot * means that we're the last holder and can skip it. Also, the refcount 6914dbb207bSFrançois Tigeot * can never increase from 1 again, so we don't need any barriers or 6924dbb207bSFrançois Tigeot * locks. 6934dbb207bSFrançois Tigeot * 6944dbb207bSFrançois Tigeot * Note that userspace could try to race with use and instate a new 6954dbb207bSFrançois Tigeot * usage _after_ we've cleared all current ones. End result will be an 6964dbb207bSFrançois Tigeot * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot 6974dbb207bSFrançois Tigeot * in this manner. 6984dbb207bSFrançois Tigeot */ 6994dbb207bSFrançois Tigeot if (atomic_read(&fb->refcount.refcount) > 1) { 7004dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 7015718399fSFrançois Tigeot /* remove from any CRTC */ 7025718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 703ba55f2f5SFrançois Tigeot if (crtc->primary->fb == fb) { 7045718399fSFrançois Tigeot /* should turn off the crtc */ 7055718399fSFrançois Tigeot memset(&set, 0, sizeof(struct drm_mode_set)); 7065718399fSFrançois Tigeot set.crtc = crtc; 7075718399fSFrançois Tigeot set.fb = NULL; 7084dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 7095718399fSFrançois Tigeot if (ret) 7105718399fSFrançois Tigeot DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 7115718399fSFrançois Tigeot } 7125718399fSFrançois Tigeot } 7135718399fSFrançois Tigeot 7145718399fSFrançois Tigeot list_for_each_entry(plane, &dev->mode_config.plane_list, head) { 71506fede5aSFrançois Tigeot if (plane->fb == fb) 71606fede5aSFrançois Tigeot drm_plane_force_disable(plane); 7175718399fSFrançois Tigeot } 7184dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 7194dbb207bSFrançois Tigeot } 720b5162e19SFrançois Tigeot 721b5162e19SFrançois Tigeot drm_framebuffer_unreference(fb); 7225718399fSFrançois Tigeot } 723b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove); 7245718399fSFrançois Tigeot 725ba55f2f5SFrançois Tigeot DEFINE_WW_CLASS(crtc_ww_class); 726ba55f2f5SFrançois Tigeot 7275718399fSFrançois Tigeot /** 728ba55f2f5SFrançois Tigeot * drm_crtc_init_with_planes - Initialise a new CRTC object with 729ba55f2f5SFrançois Tigeot * specified primary and cursor planes. 7305718399fSFrançois Tigeot * @dev: DRM device 7315718399fSFrançois Tigeot * @crtc: CRTC object to init 732ba55f2f5SFrançois Tigeot * @primary: Primary plane for CRTC 733ba55f2f5SFrançois Tigeot * @cursor: Cursor plane for CRTC 7345718399fSFrançois Tigeot * @funcs: callbacks for the new CRTC 7355718399fSFrançois Tigeot * 73606fede5aSFrançois Tigeot * Inits a new object created as base part of a driver crtc object. 7375718399fSFrançois Tigeot * 738ba55f2f5SFrançois Tigeot * Returns: 7395718399fSFrançois Tigeot * Zero on success, error code on failure. 7405718399fSFrançois Tigeot */ 741ba55f2f5SFrançois Tigeot int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, 742ba55f2f5SFrançois Tigeot struct drm_plane *primary, 743*24edb884SFrançois Tigeot struct drm_plane *cursor, 7445718399fSFrançois Tigeot const struct drm_crtc_funcs *funcs) 7455718399fSFrançois Tigeot { 746ba55f2f5SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 7475718399fSFrançois Tigeot int ret; 7485718399fSFrançois Tigeot 7495718399fSFrançois Tigeot crtc->dev = dev; 7505718399fSFrançois Tigeot crtc->funcs = funcs; 751b5162e19SFrançois Tigeot crtc->invert_dimensions = false; 7525718399fSFrançois Tigeot 7534dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 754ba55f2f5SFrançois Tigeot drm_modeset_lock_init(&crtc->mutex); 755ba55f2f5SFrançois Tigeot /* dropped by _unlock_all(): */ 756ba55f2f5SFrançois Tigeot drm_modeset_lock(&crtc->mutex, config->acquire_ctx); 757b5162e19SFrançois Tigeot 7585718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 7595718399fSFrançois Tigeot if (ret) 7605718399fSFrançois Tigeot goto out; 7615718399fSFrançois Tigeot 762b5162e19SFrançois Tigeot crtc->base.properties = &crtc->properties; 763b5162e19SFrançois Tigeot 764ba55f2f5SFrançois Tigeot list_add_tail(&crtc->head, &config->crtc_list); 765ba55f2f5SFrançois Tigeot config->num_crtc++; 766ba55f2f5SFrançois Tigeot 767ba55f2f5SFrançois Tigeot crtc->primary = primary; 768*24edb884SFrançois Tigeot crtc->cursor = cursor; 769ba55f2f5SFrançois Tigeot if (primary) 770ba55f2f5SFrançois Tigeot primary->possible_crtcs = 1 << drm_crtc_index(crtc); 771*24edb884SFrançois Tigeot if (cursor) 772*24edb884SFrançois Tigeot cursor->possible_crtcs = 1 << drm_crtc_index(crtc); 773b5162e19SFrançois Tigeot 7745718399fSFrançois Tigeot out: 7754dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 7765718399fSFrançois Tigeot 7775718399fSFrançois Tigeot return ret; 7785718399fSFrançois Tigeot } 779ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init_with_planes); 7805718399fSFrançois Tigeot 7815718399fSFrançois Tigeot /** 78206fede5aSFrançois Tigeot * drm_crtc_cleanup - Clean up the core crtc usage 7835718399fSFrançois Tigeot * @crtc: CRTC to cleanup 7845718399fSFrançois Tigeot * 78506fede5aSFrançois Tigeot * This function cleans up @crtc and removes it from the DRM mode setting 78606fede5aSFrançois Tigeot * core. Note that the function does *not* free the crtc structure itself, 78706fede5aSFrançois Tigeot * this is the responsibility of the caller. 7885718399fSFrançois Tigeot */ 7895718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc) 7905718399fSFrançois Tigeot { 7915718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 7925718399fSFrançois Tigeot 7934dbb207bSFrançois Tigeot kfree(crtc->gamma_store); 7945718399fSFrançois Tigeot crtc->gamma_store = NULL; 7955718399fSFrançois Tigeot 796ba55f2f5SFrançois Tigeot drm_modeset_lock_fini(&crtc->mutex); 797ba55f2f5SFrançois Tigeot 7985718399fSFrançois Tigeot drm_mode_object_put(dev, &crtc->base); 7995718399fSFrançois Tigeot list_del(&crtc->head); 8005718399fSFrançois Tigeot dev->mode_config.num_crtc--; 8015718399fSFrançois Tigeot } 802b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup); 8035718399fSFrançois Tigeot 8045718399fSFrançois Tigeot /** 8059edbd4a0SFrançois Tigeot * drm_crtc_index - find the index of a registered CRTC 8069edbd4a0SFrançois Tigeot * @crtc: CRTC to find index for 8079edbd4a0SFrançois Tigeot * 8089edbd4a0SFrançois Tigeot * Given a registered CRTC, return the index of that CRTC within a DRM 8099edbd4a0SFrançois Tigeot * device's list of CRTCs. 8109edbd4a0SFrançois Tigeot */ 8119edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc) 8129edbd4a0SFrançois Tigeot { 8139edbd4a0SFrançois Tigeot unsigned int index = 0; 8149edbd4a0SFrançois Tigeot struct drm_crtc *tmp; 8159edbd4a0SFrançois Tigeot 8169edbd4a0SFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { 8179edbd4a0SFrançois Tigeot if (tmp == crtc) 8189edbd4a0SFrançois Tigeot return index; 8199edbd4a0SFrançois Tigeot 8209edbd4a0SFrançois Tigeot index++; 8219edbd4a0SFrançois Tigeot } 8229edbd4a0SFrançois Tigeot 8239edbd4a0SFrançois Tigeot BUG(); 8249edbd4a0SFrançois Tigeot } 8259edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index); 8269edbd4a0SFrançois Tigeot 8279edbd4a0SFrançois Tigeot /* 8285718399fSFrançois Tigeot * drm_mode_remove - remove and free a mode 8295718399fSFrançois Tigeot * @connector: connector list to modify 8305718399fSFrançois Tigeot * @mode: mode to remove 8315718399fSFrançois Tigeot * 8325718399fSFrançois Tigeot * Remove @mode from @connector's mode list, then free it. 8335718399fSFrançois Tigeot */ 8349edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector, 8355718399fSFrançois Tigeot struct drm_display_mode *mode) 8365718399fSFrançois Tigeot { 8375718399fSFrançois Tigeot list_del(&mode->head); 8385718399fSFrançois Tigeot drm_mode_destroy(connector->dev, mode); 8395718399fSFrançois Tigeot } 8405718399fSFrançois Tigeot 8415718399fSFrançois Tigeot /** 8425718399fSFrançois Tigeot * drm_connector_init - Init a preallocated connector 8435718399fSFrançois Tigeot * @dev: DRM device 8445718399fSFrançois Tigeot * @connector: the connector to init 8455718399fSFrançois Tigeot * @funcs: callbacks for this connector 846a2fdbec6SFrançois Tigeot * @connector_type: user visible type of the connector 8475718399fSFrançois Tigeot * 8485718399fSFrançois Tigeot * Initialises a preallocated connector. Connectors should be 8495718399fSFrançois Tigeot * subclassed as part of driver connector objects. 8505718399fSFrançois Tigeot * 851ba55f2f5SFrançois Tigeot * Returns: 8525718399fSFrançois Tigeot * Zero on success, error code on failure. 8535718399fSFrançois Tigeot */ 8545718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev, 8555718399fSFrançois Tigeot struct drm_connector *connector, 8565718399fSFrançois Tigeot const struct drm_connector_funcs *funcs, 8575718399fSFrançois Tigeot int connector_type) 8585718399fSFrançois Tigeot { 8595718399fSFrançois Tigeot int ret; 8605718399fSFrançois Tigeot 8614dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 8625718399fSFrançois Tigeot 8635718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); 8645718399fSFrançois Tigeot if (ret) 865ba55f2f5SFrançois Tigeot goto out_unlock; 8665718399fSFrançois Tigeot 867b5162e19SFrançois Tigeot connector->base.properties = &connector->properties; 8685718399fSFrançois Tigeot connector->dev = dev; 8695718399fSFrançois Tigeot connector->funcs = funcs; 8705718399fSFrançois Tigeot connector->connector_type = connector_type; 8715718399fSFrançois Tigeot connector->connector_type_id = 8725718399fSFrançois Tigeot ++drm_connector_enum_list[connector_type].count; /* TODO */ 8739edbd4a0SFrançois Tigeot if (connector->connector_type_id < 0) { 8749edbd4a0SFrançois Tigeot ret = connector->connector_type_id; 875ba55f2f5SFrançois Tigeot goto out_put; 8769edbd4a0SFrançois Tigeot } 877b4efbf42Szrj connector->name = 878b4efbf42Szrj drm_asprintf(GFP_KERNEL, "%s-%d", 879b4efbf42Szrj drm_connector_enum_list[connector_type].name, 880b4efbf42Szrj connector->connector_type_id); 881b4efbf42Szrj if (!connector->name) { 882b4efbf42Szrj ret = -ENOMEM; 883b4efbf42Szrj goto out_put; 884b4efbf42Szrj } 885ba55f2f5SFrançois Tigeot 8865718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->probed_modes); 8875718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->modes); 8885718399fSFrançois Tigeot connector->edid_blob_ptr = NULL; 889b5162e19SFrançois Tigeot connector->status = connector_status_unknown; 8905718399fSFrançois Tigeot 8915718399fSFrançois Tigeot list_add_tail(&connector->head, &dev->mode_config.connector_list); 8925718399fSFrançois Tigeot dev->mode_config.num_connector++; 8935718399fSFrançois Tigeot 894b5162e19SFrançois Tigeot if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) 895b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 896b5162e19SFrançois Tigeot dev->mode_config.edid_property, 897b5162e19SFrançois Tigeot 0); 8985718399fSFrançois Tigeot 899b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 9005718399fSFrançois Tigeot dev->mode_config.dpms_property, 0); 9015718399fSFrançois Tigeot 902*24edb884SFrançois Tigeot connector->debugfs_entry = NULL; 903*24edb884SFrançois Tigeot 904ba55f2f5SFrançois Tigeot out_put: 905ba55f2f5SFrançois Tigeot if (ret) 906ba55f2f5SFrançois Tigeot drm_mode_object_put(dev, &connector->base); 907ba55f2f5SFrançois Tigeot 908ba55f2f5SFrançois Tigeot out_unlock: 9094dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 9105718399fSFrançois Tigeot 9115718399fSFrançois Tigeot return ret; 9125718399fSFrançois Tigeot } 913b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init); 9145718399fSFrançois Tigeot 9155718399fSFrançois Tigeot /** 9165718399fSFrançois Tigeot * drm_connector_cleanup - cleans up an initialised connector 9175718399fSFrançois Tigeot * @connector: connector to cleanup 9185718399fSFrançois Tigeot * 9195718399fSFrançois Tigeot * Cleans up the connector but doesn't free the object. 9205718399fSFrançois Tigeot */ 9215718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector) 9225718399fSFrançois Tigeot { 9235718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 9245718399fSFrançois Tigeot struct drm_display_mode *mode, *t; 9255718399fSFrançois Tigeot 9265718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 9275718399fSFrançois Tigeot drm_mode_remove(connector, mode); 9285718399fSFrançois Tigeot 9295718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->modes, head) 9305718399fSFrançois Tigeot drm_mode_remove(connector, mode); 9315718399fSFrançois Tigeot 9325718399fSFrançois Tigeot drm_mode_object_put(dev, &connector->base); 933b4efbf42Szrj kfree(connector->name); 934b4efbf42Szrj connector->name = NULL; 9355718399fSFrançois Tigeot list_del(&connector->head); 9365718399fSFrançois Tigeot dev->mode_config.num_connector--; 9375718399fSFrançois Tigeot } 938b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup); 939b5162e19SFrançois Tigeot 940ba55f2f5SFrançois Tigeot /** 941c6f73aabSFrançois Tigeot * drm_connector_register - register a connector 942c6f73aabSFrançois Tigeot * @connector: the connector to register 943c6f73aabSFrançois Tigeot * 944c6f73aabSFrançois Tigeot * Register userspace interfaces for a connector 945c6f73aabSFrançois Tigeot * 946c6f73aabSFrançois Tigeot * Returns: 947c6f73aabSFrançois Tigeot * Zero on success, error code on failure. 948c6f73aabSFrançois Tigeot */ 949c6f73aabSFrançois Tigeot int drm_connector_register(struct drm_connector *connector) 950c6f73aabSFrançois Tigeot { 951*24edb884SFrançois Tigeot int ret; 952*24edb884SFrançois Tigeot 953*24edb884SFrançois Tigeot ret = drm_sysfs_connector_add(connector); 954*24edb884SFrançois Tigeot if (ret) 955*24edb884SFrançois Tigeot return ret; 956*24edb884SFrançois Tigeot 957*24edb884SFrançois Tigeot #if 0 958*24edb884SFrançois Tigeot ret = drm_debugfs_connector_add(connector); 959*24edb884SFrançois Tigeot #endif 960*24edb884SFrançois Tigeot if (ret) { 961*24edb884SFrançois Tigeot drm_sysfs_connector_remove(connector); 962*24edb884SFrançois Tigeot return ret; 963*24edb884SFrançois Tigeot } 964*24edb884SFrançois Tigeot 965*24edb884SFrançois Tigeot return 0; 966c6f73aabSFrançois Tigeot } 967c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_register); 968c6f73aabSFrançois Tigeot 969c6f73aabSFrançois Tigeot /** 970c6f73aabSFrançois Tigeot * drm_connector_unregister - unregister a connector 971c6f73aabSFrançois Tigeot * @connector: the connector to unregister 972c6f73aabSFrançois Tigeot * 973c6f73aabSFrançois Tigeot * Unregister userspace interfaces for a connector 974c6f73aabSFrançois Tigeot */ 975c6f73aabSFrançois Tigeot void drm_connector_unregister(struct drm_connector *connector) 976c6f73aabSFrançois Tigeot { 977c6f73aabSFrançois Tigeot drm_sysfs_connector_remove(connector); 978*24edb884SFrançois Tigeot #if 0 979*24edb884SFrançois Tigeot drm_debugfs_connector_remove(connector); 980*24edb884SFrançois Tigeot #endif 981c6f73aabSFrançois Tigeot } 982c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister); 983c6f73aabSFrançois Tigeot 984c6f73aabSFrançois Tigeot 985c6f73aabSFrançois Tigeot /** 986ba55f2f5SFrançois Tigeot * drm_connector_unplug_all - unregister connector userspace interfaces 987ba55f2f5SFrançois Tigeot * @dev: drm device 988ba55f2f5SFrançois Tigeot * 989ba55f2f5SFrançois Tigeot * This function unregisters all connector userspace interfaces in sysfs. Should 990ba55f2f5SFrançois Tigeot * be call when the device is disconnected, e.g. from an usb driver's 991ba55f2f5SFrançois Tigeot * ->disconnect callback. 992ba55f2f5SFrançois Tigeot */ 993b5162e19SFrançois Tigeot void drm_connector_unplug_all(struct drm_device *dev) 994b5162e19SFrançois Tigeot { 995b5162e19SFrançois Tigeot struct drm_connector *connector; 996b5162e19SFrançois Tigeot 997b5162e19SFrançois Tigeot /* taking the mode config mutex ends up in a clash with sysfs */ 998b5162e19SFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 999c6f73aabSFrançois Tigeot drm_connector_unregister(connector); 10004dbb207bSFrançois Tigeot 1001b5162e19SFrançois Tigeot } 1002b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unplug_all); 10035718399fSFrançois Tigeot 1004ba55f2f5SFrançois Tigeot /** 1005ba55f2f5SFrançois Tigeot * drm_bridge_init - initialize a drm transcoder/bridge 1006ba55f2f5SFrançois Tigeot * @dev: drm device 1007ba55f2f5SFrançois Tigeot * @bridge: transcoder/bridge to set up 1008ba55f2f5SFrançois Tigeot * @funcs: bridge function table 1009ba55f2f5SFrançois Tigeot * 1010ba55f2f5SFrançois Tigeot * Initialises a preallocated bridge. Bridges should be 1011ba55f2f5SFrançois Tigeot * subclassed as part of driver connector objects. 1012ba55f2f5SFrançois Tigeot * 1013ba55f2f5SFrançois Tigeot * Returns: 1014ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 1015ba55f2f5SFrançois Tigeot */ 1016ba55f2f5SFrançois Tigeot int drm_bridge_init(struct drm_device *dev, struct drm_bridge *bridge, 1017ba55f2f5SFrançois Tigeot const struct drm_bridge_funcs *funcs) 1018ba55f2f5SFrançois Tigeot { 1019ba55f2f5SFrançois Tigeot int ret; 1020ba55f2f5SFrançois Tigeot 1021ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 1022ba55f2f5SFrançois Tigeot 1023ba55f2f5SFrançois Tigeot ret = drm_mode_object_get(dev, &bridge->base, DRM_MODE_OBJECT_BRIDGE); 1024ba55f2f5SFrançois Tigeot if (ret) 1025ba55f2f5SFrançois Tigeot goto out; 1026ba55f2f5SFrançois Tigeot 1027ba55f2f5SFrançois Tigeot bridge->dev = dev; 1028ba55f2f5SFrançois Tigeot bridge->funcs = funcs; 1029ba55f2f5SFrançois Tigeot 1030ba55f2f5SFrançois Tigeot list_add_tail(&bridge->head, &dev->mode_config.bridge_list); 1031ba55f2f5SFrançois Tigeot dev->mode_config.num_bridge++; 1032ba55f2f5SFrançois Tigeot 1033ba55f2f5SFrançois Tigeot out: 1034ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 1035ba55f2f5SFrançois Tigeot return ret; 1036ba55f2f5SFrançois Tigeot } 1037ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_bridge_init); 1038ba55f2f5SFrançois Tigeot 1039ba55f2f5SFrançois Tigeot /** 1040ba55f2f5SFrançois Tigeot * drm_bridge_cleanup - cleans up an initialised bridge 1041ba55f2f5SFrançois Tigeot * @bridge: bridge to cleanup 1042ba55f2f5SFrançois Tigeot * 1043ba55f2f5SFrançois Tigeot * Cleans up the bridge but doesn't free the object. 1044ba55f2f5SFrançois Tigeot */ 1045ba55f2f5SFrançois Tigeot void drm_bridge_cleanup(struct drm_bridge *bridge) 1046ba55f2f5SFrançois Tigeot { 1047ba55f2f5SFrançois Tigeot struct drm_device *dev = bridge->dev; 1048ba55f2f5SFrançois Tigeot 1049ba55f2f5SFrançois Tigeot drm_modeset_lock_all(dev); 1050ba55f2f5SFrançois Tigeot drm_mode_object_put(dev, &bridge->base); 1051ba55f2f5SFrançois Tigeot list_del(&bridge->head); 1052ba55f2f5SFrançois Tigeot dev->mode_config.num_bridge--; 1053ba55f2f5SFrançois Tigeot drm_modeset_unlock_all(dev); 1054ba55f2f5SFrançois Tigeot } 1055ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_bridge_cleanup); 1056ba55f2f5SFrançois Tigeot 1057ba55f2f5SFrançois Tigeot /** 1058ba55f2f5SFrançois Tigeot * drm_encoder_init - Init a preallocated encoder 1059ba55f2f5SFrançois Tigeot * @dev: drm device 1060ba55f2f5SFrançois Tigeot * @encoder: the encoder to init 1061ba55f2f5SFrançois Tigeot * @funcs: callbacks for this encoder 1062ba55f2f5SFrançois Tigeot * @encoder_type: user visible type of the encoder 1063ba55f2f5SFrançois Tigeot * 1064ba55f2f5SFrançois Tigeot * Initialises a preallocated encoder. Encoder should be 1065ba55f2f5SFrançois Tigeot * subclassed as part of driver encoder objects. 1066ba55f2f5SFrançois Tigeot * 1067ba55f2f5SFrançois Tigeot * Returns: 1068ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 1069ba55f2f5SFrançois Tigeot */ 10705718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev, 10715718399fSFrançois Tigeot struct drm_encoder *encoder, 10725718399fSFrançois Tigeot const struct drm_encoder_funcs *funcs, 10735718399fSFrançois Tigeot int encoder_type) 10745718399fSFrançois Tigeot { 10755718399fSFrançois Tigeot int ret; 10765718399fSFrançois Tigeot 10774dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 10785718399fSFrançois Tigeot 10795718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); 10805718399fSFrançois Tigeot if (ret) 1081ba55f2f5SFrançois Tigeot goto out_unlock; 10825718399fSFrançois Tigeot 10835718399fSFrançois Tigeot encoder->dev = dev; 10845718399fSFrançois Tigeot encoder->encoder_type = encoder_type; 10855718399fSFrançois Tigeot encoder->funcs = funcs; 1086b4efbf42Szrj encoder->name = drm_asprintf(GFP_KERNEL, "%s-%d", 1087b4efbf42Szrj drm_encoder_enum_list[encoder_type].name, 1088b4efbf42Szrj encoder->base.id); 1089b4efbf42Szrj if (!encoder->name) { 1090b4efbf42Szrj ret = -ENOMEM; 1091b4efbf42Szrj goto out_put; 1092b4efbf42Szrj } 10935718399fSFrançois Tigeot 10945718399fSFrançois Tigeot list_add_tail(&encoder->head, &dev->mode_config.encoder_list); 10955718399fSFrançois Tigeot dev->mode_config.num_encoder++; 10965718399fSFrançois Tigeot 1097b4efbf42Szrj out_put: 1098b4efbf42Szrj if (ret) 1099b4efbf42Szrj drm_mode_object_put(dev, &encoder->base); 1100b4efbf42Szrj 1101ba55f2f5SFrançois Tigeot out_unlock: 11024dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 11035718399fSFrançois Tigeot 11045718399fSFrançois Tigeot return ret; 11055718399fSFrançois Tigeot } 1106b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init); 11075718399fSFrançois Tigeot 1108ba55f2f5SFrançois Tigeot /** 1109ba55f2f5SFrançois Tigeot * drm_encoder_cleanup - cleans up an initialised encoder 1110ba55f2f5SFrançois Tigeot * @encoder: encoder to cleanup 1111ba55f2f5SFrançois Tigeot * 1112ba55f2f5SFrançois Tigeot * Cleans up the encoder but doesn't free the object. 1113ba55f2f5SFrançois Tigeot */ 11145718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder) 11155718399fSFrançois Tigeot { 11165718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 11174dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 11185718399fSFrançois Tigeot drm_mode_object_put(dev, &encoder->base); 1119ba55f2f5SFrançois Tigeot kfree(encoder->name); 1120ba55f2f5SFrançois Tigeot encoder->name = NULL; 11215718399fSFrançois Tigeot list_del(&encoder->head); 11225718399fSFrançois Tigeot dev->mode_config.num_encoder--; 11234dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 11245718399fSFrançois Tigeot } 1125b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup); 11265718399fSFrançois Tigeot 112706fede5aSFrançois Tigeot /** 1128ba55f2f5SFrançois Tigeot * drm_universal_plane_init - Initialize a new universal plane object 112906fede5aSFrançois Tigeot * @dev: DRM device 113006fede5aSFrançois Tigeot * @plane: plane object to init 113106fede5aSFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 113206fede5aSFrançois Tigeot * @funcs: callbacks for the new plane 113306fede5aSFrançois Tigeot * @formats: array of supported formats (%DRM_FORMAT_*) 113406fede5aSFrançois Tigeot * @format_count: number of elements in @formats 1135ba55f2f5SFrançois Tigeot * @type: type of plane (overlay, primary, cursor) 113606fede5aSFrançois Tigeot * 1137ba55f2f5SFrançois Tigeot * Initializes a plane object of type @type. 113806fede5aSFrançois Tigeot * 1139ba55f2f5SFrançois Tigeot * Returns: 114006fede5aSFrançois Tigeot * Zero on success, error code on failure. 114106fede5aSFrançois Tigeot */ 1142ba55f2f5SFrançois Tigeot int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, 11435718399fSFrançois Tigeot unsigned long possible_crtcs, 11445718399fSFrançois Tigeot const struct drm_plane_funcs *funcs, 11455718399fSFrançois Tigeot const uint32_t *formats, uint32_t format_count, 1146ba55f2f5SFrançois Tigeot enum drm_plane_type type) 11475718399fSFrançois Tigeot { 11485718399fSFrançois Tigeot int ret; 11495718399fSFrançois Tigeot 11504dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 11515718399fSFrançois Tigeot 11525718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 11535718399fSFrançois Tigeot if (ret) 11545718399fSFrançois Tigeot goto out; 11555718399fSFrançois Tigeot 1156b5162e19SFrançois Tigeot plane->base.properties = &plane->properties; 11575718399fSFrançois Tigeot plane->dev = dev; 11585718399fSFrançois Tigeot plane->funcs = funcs; 11595718399fSFrançois Tigeot plane->format_types = kmalloc(sizeof(uint32_t) * format_count, 11605a3b77d5SFrançois Tigeot M_DRM, M_WAITOK); 1161b5162e19SFrançois Tigeot if (!plane->format_types) { 1162b5162e19SFrançois Tigeot DRM_DEBUG_KMS("out of memory when allocating plane\n"); 1163b5162e19SFrançois Tigeot drm_mode_object_put(dev, &plane->base); 1164b5162e19SFrançois Tigeot ret = -ENOMEM; 1165b5162e19SFrançois Tigeot goto out; 1166b5162e19SFrançois Tigeot } 11675718399fSFrançois Tigeot 11685718399fSFrançois Tigeot memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 11695718399fSFrançois Tigeot plane->format_count = format_count; 11705718399fSFrançois Tigeot plane->possible_crtcs = possible_crtcs; 1171ba55f2f5SFrançois Tigeot plane->type = type; 11725718399fSFrançois Tigeot 11735718399fSFrançois Tigeot list_add_tail(&plane->head, &dev->mode_config.plane_list); 1174ba55f2f5SFrançois Tigeot dev->mode_config.num_total_plane++; 1175ba55f2f5SFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 1176ba55f2f5SFrançois Tigeot dev->mode_config.num_overlay_plane++; 1177ba55f2f5SFrançois Tigeot 1178ba55f2f5SFrançois Tigeot drm_object_attach_property(&plane->base, 1179ba55f2f5SFrançois Tigeot dev->mode_config.plane_type_property, 1180ba55f2f5SFrançois Tigeot plane->type); 11815718399fSFrançois Tigeot 11825718399fSFrançois Tigeot out: 11834dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 11845718399fSFrançois Tigeot 11855718399fSFrançois Tigeot return ret; 11865718399fSFrançois Tigeot } 1187ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_universal_plane_init); 1188ba55f2f5SFrançois Tigeot 1189ba55f2f5SFrançois Tigeot /** 1190ba55f2f5SFrançois Tigeot * drm_plane_init - Initialize a legacy plane 1191ba55f2f5SFrançois Tigeot * @dev: DRM device 1192ba55f2f5SFrançois Tigeot * @plane: plane object to init 1193ba55f2f5SFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 1194ba55f2f5SFrançois Tigeot * @funcs: callbacks for the new plane 1195ba55f2f5SFrançois Tigeot * @formats: array of supported formats (%DRM_FORMAT_*) 1196ba55f2f5SFrançois Tigeot * @format_count: number of elements in @formats 1197ba55f2f5SFrançois Tigeot * @is_primary: plane type (primary vs overlay) 1198ba55f2f5SFrançois Tigeot * 1199ba55f2f5SFrançois Tigeot * Legacy API to initialize a DRM plane. 1200ba55f2f5SFrançois Tigeot * 1201ba55f2f5SFrançois Tigeot * New drivers should call drm_universal_plane_init() instead. 1202ba55f2f5SFrançois Tigeot * 1203ba55f2f5SFrançois Tigeot * Returns: 1204ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 1205ba55f2f5SFrançois Tigeot */ 1206ba55f2f5SFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 1207ba55f2f5SFrançois Tigeot unsigned long possible_crtcs, 1208ba55f2f5SFrançois Tigeot const struct drm_plane_funcs *funcs, 1209ba55f2f5SFrançois Tigeot const uint32_t *formats, uint32_t format_count, 1210ba55f2f5SFrançois Tigeot bool is_primary) 1211ba55f2f5SFrançois Tigeot { 1212ba55f2f5SFrançois Tigeot enum drm_plane_type type; 1213ba55f2f5SFrançois Tigeot 1214ba55f2f5SFrançois Tigeot type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; 1215ba55f2f5SFrançois Tigeot return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, 1216ba55f2f5SFrançois Tigeot formats, format_count, type); 1217ba55f2f5SFrançois Tigeot } 1218b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init); 12195718399fSFrançois Tigeot 122006fede5aSFrançois Tigeot /** 122106fede5aSFrançois Tigeot * drm_plane_cleanup - Clean up the core plane usage 122206fede5aSFrançois Tigeot * @plane: plane to cleanup 122306fede5aSFrançois Tigeot * 122406fede5aSFrançois Tigeot * This function cleans up @plane and removes it from the DRM mode setting 122506fede5aSFrançois Tigeot * core. Note that the function does *not* free the plane structure itself, 122606fede5aSFrançois Tigeot * this is the responsibility of the caller. 122706fede5aSFrançois Tigeot */ 12285718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane) 12295718399fSFrançois Tigeot { 12305718399fSFrançois Tigeot struct drm_device *dev = plane->dev; 12315718399fSFrançois Tigeot 12324dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 12334dbb207bSFrançois Tigeot kfree(plane->format_types); 12345718399fSFrançois Tigeot drm_mode_object_put(dev, &plane->base); 1235ba55f2f5SFrançois Tigeot 1236ba55f2f5SFrançois Tigeot BUG_ON(list_empty(&plane->head)); 1237ba55f2f5SFrançois Tigeot 12385718399fSFrançois Tigeot list_del(&plane->head); 1239ba55f2f5SFrançois Tigeot dev->mode_config.num_total_plane--; 1240ba55f2f5SFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 1241ba55f2f5SFrançois Tigeot dev->mode_config.num_overlay_plane--; 12424dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 12435718399fSFrançois Tigeot } 1244b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup); 12455718399fSFrançois Tigeot 12465718399fSFrançois Tigeot /** 124706fede5aSFrançois Tigeot * drm_plane_force_disable - Forcibly disable a plane 124806fede5aSFrançois Tigeot * @plane: plane to disable 124906fede5aSFrançois Tigeot * 125006fede5aSFrançois Tigeot * Forces the plane to be disabled. 125106fede5aSFrançois Tigeot * 125206fede5aSFrançois Tigeot * Used when the plane's current framebuffer is destroyed, 125306fede5aSFrançois Tigeot * and when restoring fbdev mode. 125406fede5aSFrançois Tigeot */ 125506fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane) 125606fede5aSFrançois Tigeot { 1257ba55f2f5SFrançois Tigeot struct drm_framebuffer *old_fb = plane->fb; 125806fede5aSFrançois Tigeot int ret; 125906fede5aSFrançois Tigeot 1260ba55f2f5SFrançois Tigeot if (!old_fb) 126106fede5aSFrançois Tigeot return; 126206fede5aSFrançois Tigeot 126306fede5aSFrançois Tigeot ret = plane->funcs->disable_plane(plane); 1264ba55f2f5SFrançois Tigeot if (ret) { 126506fede5aSFrançois Tigeot DRM_ERROR("failed to disable plane with busy fb\n"); 1266ba55f2f5SFrançois Tigeot return; 1267ba55f2f5SFrançois Tigeot } 126806fede5aSFrançois Tigeot /* disconnect the plane from the fb and crtc: */ 1269ba55f2f5SFrançois Tigeot __drm_framebuffer_unreference(old_fb); 127006fede5aSFrançois Tigeot plane->fb = NULL; 127106fede5aSFrançois Tigeot plane->crtc = NULL; 127206fede5aSFrançois Tigeot } 127306fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable); 127406fede5aSFrançois Tigeot 12755718399fSFrançois Tigeot static int drm_mode_create_standard_connector_properties(struct drm_device *dev) 12765718399fSFrançois Tigeot { 12775718399fSFrançois Tigeot struct drm_property *edid; 12785718399fSFrançois Tigeot struct drm_property *dpms; 1279*24edb884SFrançois Tigeot struct drm_property *dev_path; 12805718399fSFrançois Tigeot 12815718399fSFrançois Tigeot /* 12825718399fSFrançois Tigeot * Standard properties (apply to all connectors) 12835718399fSFrançois Tigeot */ 12845718399fSFrançois Tigeot edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | 12855718399fSFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 12865718399fSFrançois Tigeot "EDID", 0); 12875718399fSFrançois Tigeot dev->mode_config.edid_property = edid; 12885718399fSFrançois Tigeot 12895718399fSFrançois Tigeot dpms = drm_property_create_enum(dev, 0, 12905718399fSFrançois Tigeot "DPMS", drm_dpms_enum_list, 1291b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dpms_enum_list)); 12925718399fSFrançois Tigeot dev->mode_config.dpms_property = dpms; 12935718399fSFrançois Tigeot 1294*24edb884SFrançois Tigeot dev_path = drm_property_create(dev, 1295*24edb884SFrançois Tigeot DRM_MODE_PROP_BLOB | 1296*24edb884SFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 1297*24edb884SFrançois Tigeot "PATH", 0); 1298*24edb884SFrançois Tigeot dev->mode_config.path_property = dev_path; 1299*24edb884SFrançois Tigeot 13005718399fSFrançois Tigeot return 0; 13015718399fSFrançois Tigeot } 13025718399fSFrançois Tigeot 1303ba55f2f5SFrançois Tigeot static int drm_mode_create_standard_plane_properties(struct drm_device *dev) 1304ba55f2f5SFrançois Tigeot { 1305ba55f2f5SFrançois Tigeot struct drm_property *type; 1306ba55f2f5SFrançois Tigeot 1307ba55f2f5SFrançois Tigeot /* 1308ba55f2f5SFrançois Tigeot * Standard properties (apply to all planes) 1309ba55f2f5SFrançois Tigeot */ 1310ba55f2f5SFrançois Tigeot type = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 1311ba55f2f5SFrançois Tigeot "type", drm_plane_type_enum_list, 1312ba55f2f5SFrançois Tigeot ARRAY_SIZE(drm_plane_type_enum_list)); 1313ba55f2f5SFrançois Tigeot dev->mode_config.plane_type_property = type; 1314ba55f2f5SFrançois Tigeot 1315ba55f2f5SFrançois Tigeot return 0; 1316ba55f2f5SFrançois Tigeot } 1317ba55f2f5SFrançois Tigeot 13185718399fSFrançois Tigeot /** 13195718399fSFrançois Tigeot * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 13205718399fSFrançois Tigeot * @dev: DRM device 13215718399fSFrançois Tigeot * 13225718399fSFrançois Tigeot * Called by a driver the first time a DVI-I connector is made. 13235718399fSFrançois Tigeot */ 13245718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev) 13255718399fSFrançois Tigeot { 13265718399fSFrançois Tigeot struct drm_property *dvi_i_selector; 13275718399fSFrançois Tigeot struct drm_property *dvi_i_subconnector; 13285718399fSFrançois Tigeot 13295718399fSFrançois Tigeot if (dev->mode_config.dvi_i_select_subconnector_property) 13305718399fSFrançois Tigeot return 0; 13315718399fSFrançois Tigeot 13325718399fSFrançois Tigeot dvi_i_selector = 13335718399fSFrançois Tigeot drm_property_create_enum(dev, 0, 13345718399fSFrançois Tigeot "select subconnector", 13355718399fSFrançois Tigeot drm_dvi_i_select_enum_list, 1336b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_select_enum_list)); 13375718399fSFrançois Tigeot dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 13385718399fSFrançois Tigeot 13395718399fSFrançois Tigeot dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 13405718399fSFrançois Tigeot "subconnector", 13415718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list, 1342b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 13435718399fSFrançois Tigeot dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 13445718399fSFrançois Tigeot 13455718399fSFrançois Tigeot return 0; 13465718399fSFrançois Tigeot } 1347b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); 13485718399fSFrançois Tigeot 13495718399fSFrançois Tigeot /** 13505718399fSFrançois Tigeot * drm_create_tv_properties - create TV specific connector properties 13515718399fSFrançois Tigeot * @dev: DRM device 13525718399fSFrançois Tigeot * @num_modes: number of different TV formats (modes) supported 13535718399fSFrançois Tigeot * @modes: array of pointers to strings containing name of each format 13545718399fSFrançois Tigeot * 13555718399fSFrançois Tigeot * Called by a driver's TV initialization routine, this function creates 13565718399fSFrançois Tigeot * the TV specific connector properties for a given device. Caller is 13575718399fSFrançois Tigeot * responsible for allocating a list of format names and passing them to 13585718399fSFrançois Tigeot * this routine. 13595718399fSFrançois Tigeot */ 13605718399fSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, 13615718399fSFrançois Tigeot char *modes[]) 13625718399fSFrançois Tigeot { 13635718399fSFrançois Tigeot struct drm_property *tv_selector; 13645718399fSFrançois Tigeot struct drm_property *tv_subconnector; 13655718399fSFrançois Tigeot int i; 13665718399fSFrançois Tigeot 13675718399fSFrançois Tigeot if (dev->mode_config.tv_select_subconnector_property) 13685718399fSFrançois Tigeot return 0; 13695718399fSFrançois Tigeot 13705718399fSFrançois Tigeot /* 13715718399fSFrançois Tigeot * Basic connector properties 13725718399fSFrançois Tigeot */ 13735718399fSFrançois Tigeot tv_selector = drm_property_create_enum(dev, 0, 13745718399fSFrançois Tigeot "select subconnector", 13755718399fSFrançois Tigeot drm_tv_select_enum_list, 1376b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_select_enum_list)); 13775718399fSFrançois Tigeot dev->mode_config.tv_select_subconnector_property = tv_selector; 13785718399fSFrançois Tigeot 13795718399fSFrançois Tigeot tv_subconnector = 13805718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 13815718399fSFrançois Tigeot "subconnector", 13825718399fSFrançois Tigeot drm_tv_subconnector_enum_list, 1383b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_subconnector_enum_list)); 13845718399fSFrançois Tigeot dev->mode_config.tv_subconnector_property = tv_subconnector; 13855718399fSFrançois Tigeot 13865718399fSFrançois Tigeot /* 13875718399fSFrançois Tigeot * Other, TV specific properties: margins & TV modes. 13885718399fSFrançois Tigeot */ 13895718399fSFrançois Tigeot dev->mode_config.tv_left_margin_property = 13905718399fSFrançois Tigeot drm_property_create_range(dev, 0, "left margin", 0, 100); 13915718399fSFrançois Tigeot 13925718399fSFrançois Tigeot dev->mode_config.tv_right_margin_property = 13935718399fSFrançois Tigeot drm_property_create_range(dev, 0, "right margin", 0, 100); 13945718399fSFrançois Tigeot 13955718399fSFrançois Tigeot dev->mode_config.tv_top_margin_property = 13965718399fSFrançois Tigeot drm_property_create_range(dev, 0, "top margin", 0, 100); 13975718399fSFrançois Tigeot 13985718399fSFrançois Tigeot dev->mode_config.tv_bottom_margin_property = 13995718399fSFrançois Tigeot drm_property_create_range(dev, 0, "bottom margin", 0, 100); 14005718399fSFrançois Tigeot 14015718399fSFrançois Tigeot dev->mode_config.tv_mode_property = 14025718399fSFrançois Tigeot drm_property_create(dev, DRM_MODE_PROP_ENUM, 14035718399fSFrançois Tigeot "mode", num_modes); 14045718399fSFrançois Tigeot for (i = 0; i < num_modes; i++) 14055718399fSFrançois Tigeot drm_property_add_enum(dev->mode_config.tv_mode_property, i, 14065718399fSFrançois Tigeot i, modes[i]); 14075718399fSFrançois Tigeot 14085718399fSFrançois Tigeot dev->mode_config.tv_brightness_property = 14095718399fSFrançois Tigeot drm_property_create_range(dev, 0, "brightness", 0, 100); 14105718399fSFrançois Tigeot 14115718399fSFrançois Tigeot dev->mode_config.tv_contrast_property = 14125718399fSFrançois Tigeot drm_property_create_range(dev, 0, "contrast", 0, 100); 14135718399fSFrançois Tigeot 14145718399fSFrançois Tigeot dev->mode_config.tv_flicker_reduction_property = 14155718399fSFrançois Tigeot drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 14165718399fSFrançois Tigeot 14175718399fSFrançois Tigeot dev->mode_config.tv_overscan_property = 14185718399fSFrançois Tigeot drm_property_create_range(dev, 0, "overscan", 0, 100); 14195718399fSFrançois Tigeot 14205718399fSFrançois Tigeot dev->mode_config.tv_saturation_property = 14215718399fSFrançois Tigeot drm_property_create_range(dev, 0, "saturation", 0, 100); 14225718399fSFrançois Tigeot 14235718399fSFrançois Tigeot dev->mode_config.tv_hue_property = 14245718399fSFrançois Tigeot drm_property_create_range(dev, 0, "hue", 0, 100); 14255718399fSFrançois Tigeot 14265718399fSFrançois Tigeot return 0; 14275718399fSFrançois Tigeot } 1428b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties); 14295718399fSFrançois Tigeot 14305718399fSFrançois Tigeot /** 14315718399fSFrançois Tigeot * drm_mode_create_scaling_mode_property - create scaling mode property 14325718399fSFrançois Tigeot * @dev: DRM device 14335718399fSFrançois Tigeot * 14345718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 14355718399fSFrançois Tigeot * connectors. 14365718399fSFrançois Tigeot */ 14375718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev) 14385718399fSFrançois Tigeot { 14395718399fSFrançois Tigeot struct drm_property *scaling_mode; 14405718399fSFrançois Tigeot 14415718399fSFrançois Tigeot if (dev->mode_config.scaling_mode_property) 14425718399fSFrançois Tigeot return 0; 14435718399fSFrançois Tigeot 14445718399fSFrançois Tigeot scaling_mode = 14455718399fSFrançois Tigeot drm_property_create_enum(dev, 0, "scaling mode", 14465718399fSFrançois Tigeot drm_scaling_mode_enum_list, 1447b5162e19SFrançois Tigeot ARRAY_SIZE(drm_scaling_mode_enum_list)); 14485718399fSFrançois Tigeot 14495718399fSFrançois Tigeot dev->mode_config.scaling_mode_property = scaling_mode; 14505718399fSFrançois Tigeot 14515718399fSFrançois Tigeot return 0; 14525718399fSFrançois Tigeot } 1453b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); 14545718399fSFrançois Tigeot 14555718399fSFrançois Tigeot /** 1456*24edb884SFrançois Tigeot * drm_mode_create_aspect_ratio_property - create aspect ratio property 1457*24edb884SFrançois Tigeot * @dev: DRM device 1458*24edb884SFrançois Tigeot * 1459*24edb884SFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 1460*24edb884SFrançois Tigeot * connectors. 1461*24edb884SFrançois Tigeot * 1462*24edb884SFrançois Tigeot * Returns: 1463*24edb884SFrançois Tigeot * Zero on success, errno on failure. 1464*24edb884SFrançois Tigeot */ 1465*24edb884SFrançois Tigeot int drm_mode_create_aspect_ratio_property(struct drm_device *dev) 1466*24edb884SFrançois Tigeot { 1467*24edb884SFrançois Tigeot if (dev->mode_config.aspect_ratio_property) 1468*24edb884SFrançois Tigeot return 0; 1469*24edb884SFrançois Tigeot 1470*24edb884SFrançois Tigeot dev->mode_config.aspect_ratio_property = 1471*24edb884SFrançois Tigeot drm_property_create_enum(dev, 0, "aspect ratio", 1472*24edb884SFrançois Tigeot drm_aspect_ratio_enum_list, 1473*24edb884SFrançois Tigeot ARRAY_SIZE(drm_aspect_ratio_enum_list)); 1474*24edb884SFrançois Tigeot 1475*24edb884SFrançois Tigeot if (dev->mode_config.aspect_ratio_property == NULL) 1476*24edb884SFrançois Tigeot return -ENOMEM; 1477*24edb884SFrançois Tigeot 1478*24edb884SFrançois Tigeot return 0; 1479*24edb884SFrançois Tigeot } 1480*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); 1481*24edb884SFrançois Tigeot 1482*24edb884SFrançois Tigeot /** 14835718399fSFrançois Tigeot * drm_mode_create_dirty_property - create dirty property 14845718399fSFrançois Tigeot * @dev: DRM device 14855718399fSFrançois Tigeot * 14865718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 14875718399fSFrançois Tigeot * connectors. 14885718399fSFrançois Tigeot */ 14895718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev) 14905718399fSFrançois Tigeot { 14915718399fSFrançois Tigeot struct drm_property *dirty_info; 14925718399fSFrançois Tigeot 14935718399fSFrançois Tigeot if (dev->mode_config.dirty_info_property) 14945718399fSFrançois Tigeot return 0; 14955718399fSFrançois Tigeot 14965718399fSFrançois Tigeot dirty_info = 14975718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 14985718399fSFrançois Tigeot "dirty", 14995718399fSFrançois Tigeot drm_dirty_info_enum_list, 1500b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dirty_info_enum_list)); 15015718399fSFrançois Tigeot dev->mode_config.dirty_info_property = dirty_info; 15025718399fSFrançois Tigeot 15035718399fSFrançois Tigeot return 0; 15045718399fSFrançois Tigeot } 1505b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property); 15065718399fSFrançois Tigeot 1507b5162e19SFrançois Tigeot static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) 15085718399fSFrançois Tigeot { 15095718399fSFrançois Tigeot uint32_t total_objects = 0; 15105718399fSFrançois Tigeot 15115718399fSFrançois Tigeot total_objects += dev->mode_config.num_crtc; 15125718399fSFrançois Tigeot total_objects += dev->mode_config.num_connector; 15135718399fSFrançois Tigeot total_objects += dev->mode_config.num_encoder; 1514ba55f2f5SFrançois Tigeot total_objects += dev->mode_config.num_bridge; 15155718399fSFrançois Tigeot 15164dbb207bSFrançois Tigeot group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); 1517b5162e19SFrançois Tigeot if (!group->id_list) 1518b5162e19SFrançois Tigeot return -ENOMEM; 15195718399fSFrançois Tigeot 15205718399fSFrançois Tigeot group->num_crtcs = 0; 15215718399fSFrançois Tigeot group->num_connectors = 0; 15225718399fSFrançois Tigeot group->num_encoders = 0; 1523ba55f2f5SFrançois Tigeot group->num_bridges = 0; 15245718399fSFrançois Tigeot return 0; 15255718399fSFrançois Tigeot } 15265718399fSFrançois Tigeot 1527ba55f2f5SFrançois Tigeot void drm_mode_group_destroy(struct drm_mode_group *group) 1528ba55f2f5SFrançois Tigeot { 1529ba55f2f5SFrançois Tigeot kfree(group->id_list); 1530ba55f2f5SFrançois Tigeot group->id_list = NULL; 1531ba55f2f5SFrançois Tigeot } 1532ba55f2f5SFrançois Tigeot 1533ba55f2f5SFrançois Tigeot /* 1534ba55f2f5SFrançois Tigeot * NOTE: Driver's shouldn't ever call drm_mode_group_init_legacy_group - it is 1535ba55f2f5SFrançois Tigeot * the drm core's responsibility to set up mode control groups. 1536ba55f2f5SFrançois Tigeot */ 15375718399fSFrançois Tigeot int drm_mode_group_init_legacy_group(struct drm_device *dev, 15385718399fSFrançois Tigeot struct drm_mode_group *group) 15395718399fSFrançois Tigeot { 15405718399fSFrançois Tigeot struct drm_crtc *crtc; 15415718399fSFrançois Tigeot struct drm_encoder *encoder; 15425718399fSFrançois Tigeot struct drm_connector *connector; 1543ba55f2f5SFrançois Tigeot struct drm_bridge *bridge; 15445718399fSFrançois Tigeot int ret; 15455718399fSFrançois Tigeot 15465718399fSFrançois Tigeot if ((ret = drm_mode_group_init(dev, group))) 15475718399fSFrançois Tigeot return ret; 15485718399fSFrançois Tigeot 15495718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 15505718399fSFrançois Tigeot group->id_list[group->num_crtcs++] = crtc->base.id; 15515718399fSFrançois Tigeot 15525718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 15535718399fSFrançois Tigeot group->id_list[group->num_crtcs + group->num_encoders++] = 15545718399fSFrançois Tigeot encoder->base.id; 15555718399fSFrançois Tigeot 15565718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 15575718399fSFrançois Tigeot group->id_list[group->num_crtcs + group->num_encoders + 15585718399fSFrançois Tigeot group->num_connectors++] = connector->base.id; 15595718399fSFrançois Tigeot 1560ba55f2f5SFrançois Tigeot list_for_each_entry(bridge, &dev->mode_config.bridge_list, head) 1561ba55f2f5SFrançois Tigeot group->id_list[group->num_crtcs + group->num_encoders + 1562ba55f2f5SFrançois Tigeot group->num_connectors + group->num_bridges++] = 1563ba55f2f5SFrançois Tigeot bridge->base.id; 1564ba55f2f5SFrançois Tigeot 15655718399fSFrançois Tigeot return 0; 15665718399fSFrançois Tigeot } 1567b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_group_init_legacy_group); 15685718399fSFrançois Tigeot 1569*24edb884SFrançois Tigeot void drm_reinit_primary_mode_group(struct drm_device *dev) 1570*24edb884SFrançois Tigeot { 1571*24edb884SFrançois Tigeot drm_modeset_lock_all(dev); 1572*24edb884SFrançois Tigeot drm_mode_group_destroy(&dev->primary->mode_group); 1573*24edb884SFrançois Tigeot drm_mode_group_init_legacy_group(dev, &dev->primary->mode_group); 1574*24edb884SFrançois Tigeot drm_modeset_unlock_all(dev); 1575*24edb884SFrançois Tigeot } 1576*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_reinit_primary_mode_group); 1577*24edb884SFrançois Tigeot 15785718399fSFrançois Tigeot /** 15795718399fSFrançois Tigeot * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo 15805718399fSFrançois Tigeot * @out: drm_mode_modeinfo struct to return to the user 15815718399fSFrançois Tigeot * @in: drm_display_mode to use 15825718399fSFrançois Tigeot * 15835718399fSFrançois Tigeot * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 15845718399fSFrançois Tigeot * the user. 15855718399fSFrançois Tigeot */ 15865718399fSFrançois Tigeot static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, 15875718399fSFrançois Tigeot const struct drm_display_mode *in) 15885718399fSFrançois Tigeot { 1589b5162e19SFrançois Tigeot WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || 15905718399fSFrançois Tigeot in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || 15915718399fSFrançois Tigeot in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || 15925718399fSFrançois Tigeot in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || 1593b5162e19SFrançois Tigeot in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, 1594b5162e19SFrançois Tigeot "timing values too large for mode info\n"); 15955718399fSFrançois Tigeot 15965718399fSFrançois Tigeot out->clock = in->clock; 15975718399fSFrançois Tigeot out->hdisplay = in->hdisplay; 15985718399fSFrançois Tigeot out->hsync_start = in->hsync_start; 15995718399fSFrançois Tigeot out->hsync_end = in->hsync_end; 16005718399fSFrançois Tigeot out->htotal = in->htotal; 16015718399fSFrançois Tigeot out->hskew = in->hskew; 16025718399fSFrançois Tigeot out->vdisplay = in->vdisplay; 16035718399fSFrançois Tigeot out->vsync_start = in->vsync_start; 16045718399fSFrançois Tigeot out->vsync_end = in->vsync_end; 16055718399fSFrançois Tigeot out->vtotal = in->vtotal; 16065718399fSFrançois Tigeot out->vscan = in->vscan; 16075718399fSFrançois Tigeot out->vrefresh = in->vrefresh; 16085718399fSFrançois Tigeot out->flags = in->flags; 16095718399fSFrançois Tigeot out->type = in->type; 16105718399fSFrançois Tigeot strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 16115718399fSFrançois Tigeot out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 16125718399fSFrançois Tigeot } 16135718399fSFrançois Tigeot 16145718399fSFrançois Tigeot /** 16159edbd4a0SFrançois Tigeot * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode 16165718399fSFrançois Tigeot * @out: drm_display_mode to return to the user 16175718399fSFrançois Tigeot * @in: drm_mode_modeinfo to use 16185718399fSFrançois Tigeot * 16195718399fSFrançois Tigeot * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 16205718399fSFrançois Tigeot * the caller. 16215718399fSFrançois Tigeot * 1622ba55f2f5SFrançois Tigeot * Returns: 16235718399fSFrançois Tigeot * Zero on success, errno on failure. 16245718399fSFrançois Tigeot */ 16255718399fSFrançois Tigeot static int drm_crtc_convert_umode(struct drm_display_mode *out, 16265718399fSFrançois Tigeot const struct drm_mode_modeinfo *in) 16275718399fSFrançois Tigeot { 16285718399fSFrançois Tigeot if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 1629b5162e19SFrançois Tigeot return -ERANGE; 16305718399fSFrançois Tigeot 16319edbd4a0SFrançois Tigeot if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) 16329edbd4a0SFrançois Tigeot return -EINVAL; 16339edbd4a0SFrançois Tigeot 16345718399fSFrançois Tigeot out->clock = in->clock; 16355718399fSFrançois Tigeot out->hdisplay = in->hdisplay; 16365718399fSFrançois Tigeot out->hsync_start = in->hsync_start; 16375718399fSFrançois Tigeot out->hsync_end = in->hsync_end; 16385718399fSFrançois Tigeot out->htotal = in->htotal; 16395718399fSFrançois Tigeot out->hskew = in->hskew; 16405718399fSFrançois Tigeot out->vdisplay = in->vdisplay; 16415718399fSFrançois Tigeot out->vsync_start = in->vsync_start; 16425718399fSFrançois Tigeot out->vsync_end = in->vsync_end; 16435718399fSFrançois Tigeot out->vtotal = in->vtotal; 16445718399fSFrançois Tigeot out->vscan = in->vscan; 16455718399fSFrançois Tigeot out->vrefresh = in->vrefresh; 16465718399fSFrançois Tigeot out->flags = in->flags; 16475718399fSFrançois Tigeot out->type = in->type; 16485718399fSFrançois Tigeot strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 16495718399fSFrançois Tigeot out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 16505718399fSFrançois Tigeot 16515718399fSFrançois Tigeot return 0; 16525718399fSFrançois Tigeot } 16535718399fSFrançois Tigeot 16545718399fSFrançois Tigeot /** 16555718399fSFrançois Tigeot * drm_mode_getresources - get graphics configuration 16564dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 16574dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 16584dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 16595718399fSFrançois Tigeot * 16605718399fSFrançois Tigeot * Construct a set of configuration description structures and return 16615718399fSFrançois Tigeot * them to the user, including CRTC, connector and framebuffer configuration. 16625718399fSFrançois Tigeot * 16635718399fSFrançois Tigeot * Called by the user via ioctl. 16645718399fSFrançois Tigeot * 1665ba55f2f5SFrançois Tigeot * Returns: 16665718399fSFrançois Tigeot * Zero on success, errno on failure. 16675718399fSFrançois Tigeot */ 16685718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data, 16695718399fSFrançois Tigeot struct drm_file *file_priv) 16705718399fSFrançois Tigeot { 16715718399fSFrançois Tigeot struct drm_mode_card_res *card_res = data; 16725718399fSFrançois Tigeot struct list_head *lh; 16735718399fSFrançois Tigeot struct drm_framebuffer *fb; 16745718399fSFrançois Tigeot struct drm_connector *connector; 16755718399fSFrançois Tigeot struct drm_crtc *crtc; 16765718399fSFrançois Tigeot struct drm_encoder *encoder; 16775718399fSFrançois Tigeot int ret = 0; 16785718399fSFrançois Tigeot int connector_count = 0; 16795718399fSFrançois Tigeot int crtc_count = 0; 16805718399fSFrançois Tigeot int fb_count = 0; 16815718399fSFrançois Tigeot int encoder_count = 0; 16825718399fSFrançois Tigeot int copied = 0, i; 16835718399fSFrançois Tigeot uint32_t __user *fb_id; 16845718399fSFrançois Tigeot uint32_t __user *crtc_id; 16855718399fSFrançois Tigeot uint32_t __user *connector_id; 16865718399fSFrançois Tigeot uint32_t __user *encoder_id; 16875718399fSFrançois Tigeot struct drm_mode_group *mode_group; 16885718399fSFrançois Tigeot 16895718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1690b5162e19SFrançois Tigeot return -EINVAL; 16915718399fSFrançois Tigeot 16925718399fSFrançois Tigeot 16934dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 16945718399fSFrançois Tigeot /* 16955718399fSFrançois Tigeot * For the non-control nodes we need to limit the list of resources 16965718399fSFrançois Tigeot * by IDs in the group list for this node 16975718399fSFrançois Tigeot */ 16985718399fSFrançois Tigeot list_for_each(lh, &file_priv->fbs) 16995718399fSFrançois Tigeot fb_count++; 17005718399fSFrançois Tigeot 17014dbb207bSFrançois Tigeot /* handle this in 4 parts */ 17024dbb207bSFrançois Tigeot /* FBs */ 17034dbb207bSFrançois Tigeot if (card_res->count_fbs >= fb_count) { 17044dbb207bSFrançois Tigeot copied = 0; 17054dbb207bSFrançois Tigeot fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; 17064dbb207bSFrançois Tigeot list_for_each_entry(fb, &file_priv->fbs, filp_head) { 17074dbb207bSFrançois Tigeot if (put_user(fb->base.id, fb_id + copied)) { 17084dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 17094dbb207bSFrançois Tigeot return -EFAULT; 17104dbb207bSFrançois Tigeot } 17114dbb207bSFrançois Tigeot copied++; 17124dbb207bSFrançois Tigeot } 17134dbb207bSFrançois Tigeot } 17144dbb207bSFrançois Tigeot card_res->count_fbs = fb_count; 17154dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 17164dbb207bSFrançois Tigeot 17174dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 1718ba55f2f5SFrançois Tigeot if (!drm_is_primary_client(file_priv)) { 1719ba55f2f5SFrançois Tigeot 1720ba55f2f5SFrançois Tigeot mode_group = NULL; 17215718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.crtc_list) 17225718399fSFrançois Tigeot crtc_count++; 17235718399fSFrançois Tigeot 17245718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.connector_list) 17255718399fSFrançois Tigeot connector_count++; 17265718399fSFrançois Tigeot 17275718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.encoder_list) 17285718399fSFrançois Tigeot encoder_count++; 17295718399fSFrançois Tigeot } else { 17305718399fSFrançois Tigeot 17315718399fSFrançois Tigeot crtc_count = mode_group->num_crtcs; 17325718399fSFrançois Tigeot connector_count = mode_group->num_connectors; 17335718399fSFrançois Tigeot encoder_count = mode_group->num_encoders; 17345718399fSFrançois Tigeot } 17355718399fSFrançois Tigeot 17365718399fSFrançois Tigeot card_res->max_height = dev->mode_config.max_height; 17375718399fSFrançois Tigeot card_res->min_height = dev->mode_config.min_height; 17385718399fSFrançois Tigeot card_res->max_width = dev->mode_config.max_width; 17395718399fSFrançois Tigeot card_res->min_width = dev->mode_config.min_width; 17405718399fSFrançois Tigeot 17415718399fSFrançois Tigeot /* CRTCs */ 17425718399fSFrançois Tigeot if (card_res->count_crtcs >= crtc_count) { 17435718399fSFrançois Tigeot copied = 0; 1744b5162e19SFrançois Tigeot crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; 1745ba55f2f5SFrançois Tigeot if (!mode_group) { 17465718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, 17475718399fSFrançois Tigeot head) { 17485718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1749b5162e19SFrançois Tigeot if (put_user(crtc->base.id, crtc_id + copied)) { 1750b5162e19SFrançois Tigeot ret = -EFAULT; 17515718399fSFrançois Tigeot goto out; 17525718399fSFrançois Tigeot } 17535718399fSFrançois Tigeot copied++; 17545718399fSFrançois Tigeot } 17555718399fSFrançois Tigeot } else { 17565718399fSFrançois Tigeot for (i = 0; i < mode_group->num_crtcs; i++) { 1757b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1758b5162e19SFrançois Tigeot crtc_id + copied)) { 1759b5162e19SFrançois Tigeot ret = -EFAULT; 17605718399fSFrançois Tigeot goto out; 17615718399fSFrançois Tigeot } 17625718399fSFrançois Tigeot copied++; 17635718399fSFrançois Tigeot } 17645718399fSFrançois Tigeot } 17655718399fSFrançois Tigeot } 17665718399fSFrançois Tigeot card_res->count_crtcs = crtc_count; 17675718399fSFrançois Tigeot 17685718399fSFrançois Tigeot /* Encoders */ 17695718399fSFrançois Tigeot if (card_res->count_encoders >= encoder_count) { 17705718399fSFrançois Tigeot copied = 0; 1771b5162e19SFrançois Tigeot encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; 1772ba55f2f5SFrançois Tigeot if (!mode_group) { 17735718399fSFrançois Tigeot list_for_each_entry(encoder, 17745718399fSFrançois Tigeot &dev->mode_config.encoder_list, 17755718399fSFrançois Tigeot head) { 17765718399fSFrançois Tigeot DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, 1777ba55f2f5SFrançois Tigeot encoder->name); 1778b5162e19SFrançois Tigeot if (put_user(encoder->base.id, encoder_id + 1779b5162e19SFrançois Tigeot copied)) { 1780b5162e19SFrançois Tigeot ret = -EFAULT; 17815718399fSFrançois Tigeot goto out; 17825718399fSFrançois Tigeot } 17835718399fSFrançois Tigeot copied++; 17845718399fSFrançois Tigeot } 17855718399fSFrançois Tigeot } else { 1786b5162e19SFrançois Tigeot for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { 1787b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1788b5162e19SFrançois Tigeot encoder_id + copied)) { 1789b5162e19SFrançois Tigeot ret = -EFAULT; 17905718399fSFrançois Tigeot goto out; 17915718399fSFrançois Tigeot } 17925718399fSFrançois Tigeot copied++; 17935718399fSFrançois Tigeot } 17945718399fSFrançois Tigeot 17955718399fSFrançois Tigeot } 17965718399fSFrançois Tigeot } 17975718399fSFrançois Tigeot card_res->count_encoders = encoder_count; 17985718399fSFrançois Tigeot 17995718399fSFrançois Tigeot /* Connectors */ 18005718399fSFrançois Tigeot if (card_res->count_connectors >= connector_count) { 18015718399fSFrançois Tigeot copied = 0; 1802b5162e19SFrançois Tigeot connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; 1803ba55f2f5SFrançois Tigeot if (!mode_group) { 18045718399fSFrançois Tigeot list_for_each_entry(connector, 18055718399fSFrançois Tigeot &dev->mode_config.connector_list, 18065718399fSFrançois Tigeot head) { 18075718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 18085718399fSFrançois Tigeot connector->base.id, 1809ba55f2f5SFrançois Tigeot connector->name); 1810b5162e19SFrançois Tigeot if (put_user(connector->base.id, 1811b5162e19SFrançois Tigeot connector_id + copied)) { 1812b5162e19SFrançois Tigeot ret = -EFAULT; 18135718399fSFrançois Tigeot goto out; 18145718399fSFrançois Tigeot } 18155718399fSFrançois Tigeot copied++; 18165718399fSFrançois Tigeot } 18175718399fSFrançois Tigeot } else { 18185718399fSFrançois Tigeot int start = mode_group->num_crtcs + 18195718399fSFrançois Tigeot mode_group->num_encoders; 18205718399fSFrançois Tigeot for (i = start; i < start + mode_group->num_connectors; i++) { 1821b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1822b5162e19SFrançois Tigeot connector_id + copied)) { 1823b5162e19SFrançois Tigeot ret = -EFAULT; 18245718399fSFrançois Tigeot goto out; 18255718399fSFrançois Tigeot } 18265718399fSFrançois Tigeot copied++; 18275718399fSFrançois Tigeot } 18285718399fSFrançois Tigeot } 18295718399fSFrançois Tigeot } 18305718399fSFrançois Tigeot card_res->count_connectors = connector_count; 18315718399fSFrançois Tigeot 18325718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, 18335718399fSFrançois Tigeot card_res->count_connectors, card_res->count_encoders); 18345718399fSFrançois Tigeot 18355718399fSFrançois Tigeot out: 18364dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 18375718399fSFrançois Tigeot return ret; 18385718399fSFrançois Tigeot } 18395718399fSFrançois Tigeot 18405718399fSFrançois Tigeot /** 18415718399fSFrançois Tigeot * drm_mode_getcrtc - get CRTC configuration 18424dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 18434dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 18444dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 18455718399fSFrançois Tigeot * 18465718399fSFrançois Tigeot * Construct a CRTC configuration structure to return to the user. 18475718399fSFrançois Tigeot * 18485718399fSFrançois Tigeot * Called by the user via ioctl. 18495718399fSFrançois Tigeot * 1850ba55f2f5SFrançois Tigeot * Returns: 18515718399fSFrançois Tigeot * Zero on success, errno on failure. 18525718399fSFrançois Tigeot */ 18535718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev, 18545718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 18555718399fSFrançois Tigeot { 18565718399fSFrançois Tigeot struct drm_mode_crtc *crtc_resp = data; 18575718399fSFrançois Tigeot struct drm_crtc *crtc; 18585718399fSFrançois Tigeot int ret = 0; 18595718399fSFrançois Tigeot 18605718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1861b5162e19SFrançois Tigeot return -EINVAL; 18625718399fSFrançois Tigeot 18634dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 18645718399fSFrançois Tigeot 1865ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_resp->crtc_id); 1866ba55f2f5SFrançois Tigeot if (!crtc) { 18679edbd4a0SFrançois Tigeot ret = -ENOENT; 18685718399fSFrançois Tigeot goto out; 18695718399fSFrançois Tigeot } 18705718399fSFrançois Tigeot 18715718399fSFrançois Tigeot crtc_resp->x = crtc->x; 18725718399fSFrançois Tigeot crtc_resp->y = crtc->y; 18735718399fSFrançois Tigeot crtc_resp->gamma_size = crtc->gamma_size; 1874ba55f2f5SFrançois Tigeot if (crtc->primary->fb) 1875ba55f2f5SFrançois Tigeot crtc_resp->fb_id = crtc->primary->fb->base.id; 18765718399fSFrançois Tigeot else 18775718399fSFrançois Tigeot crtc_resp->fb_id = 0; 18785718399fSFrançois Tigeot 18795718399fSFrançois Tigeot if (crtc->enabled) { 18805718399fSFrançois Tigeot 18815718399fSFrançois Tigeot drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); 18825718399fSFrançois Tigeot crtc_resp->mode_valid = 1; 18835718399fSFrançois Tigeot 18845718399fSFrançois Tigeot } else { 18855718399fSFrançois Tigeot crtc_resp->mode_valid = 0; 18865718399fSFrançois Tigeot } 18875718399fSFrançois Tigeot 18885718399fSFrançois Tigeot out: 18894dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 18905718399fSFrançois Tigeot return ret; 18915718399fSFrançois Tigeot } 18925718399fSFrançois Tigeot 18939edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, 18949edbd4a0SFrançois Tigeot const struct drm_file *file_priv) 18959edbd4a0SFrançois Tigeot { 18969edbd4a0SFrançois Tigeot /* 18979edbd4a0SFrançois Tigeot * If user-space hasn't configured the driver to expose the stereo 3D 18989edbd4a0SFrançois Tigeot * modes, don't expose them. 18999edbd4a0SFrançois Tigeot */ 19009edbd4a0SFrançois Tigeot if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) 19019edbd4a0SFrançois Tigeot return false; 19029edbd4a0SFrançois Tigeot 19039edbd4a0SFrançois Tigeot return true; 19049edbd4a0SFrançois Tigeot } 19059edbd4a0SFrançois Tigeot 19065718399fSFrançois Tigeot /** 19075718399fSFrançois Tigeot * drm_mode_getconnector - get connector configuration 19084dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 19094dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 19104dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 19115718399fSFrançois Tigeot * 19125718399fSFrançois Tigeot * Construct a connector configuration structure to return to the user. 19135718399fSFrançois Tigeot * 19145718399fSFrançois Tigeot * Called by the user via ioctl. 19155718399fSFrançois Tigeot * 1916ba55f2f5SFrançois Tigeot * Returns: 19175718399fSFrançois Tigeot * Zero on success, errno on failure. 19185718399fSFrançois Tigeot */ 19195718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data, 19205718399fSFrançois Tigeot struct drm_file *file_priv) 19215718399fSFrançois Tigeot { 19225718399fSFrançois Tigeot struct drm_mode_get_connector *out_resp = data; 19235718399fSFrançois Tigeot struct drm_connector *connector; 19245718399fSFrançois Tigeot struct drm_display_mode *mode; 19255718399fSFrançois Tigeot int mode_count = 0; 19265718399fSFrançois Tigeot int props_count = 0; 19275718399fSFrançois Tigeot int encoders_count = 0; 19285718399fSFrançois Tigeot int ret = 0; 19295718399fSFrançois Tigeot int copied = 0; 19305718399fSFrançois Tigeot int i; 19315718399fSFrançois Tigeot struct drm_mode_modeinfo u_mode; 19325718399fSFrançois Tigeot struct drm_mode_modeinfo __user *mode_ptr; 1933b5162e19SFrançois Tigeot uint32_t __user *prop_ptr; 1934b5162e19SFrançois Tigeot uint64_t __user *prop_values; 1935b5162e19SFrançois Tigeot uint32_t __user *encoder_ptr; 19365718399fSFrançois Tigeot 19375718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1938b5162e19SFrançois Tigeot return -EINVAL; 19395718399fSFrançois Tigeot 19405718399fSFrançois Tigeot memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 19415718399fSFrançois Tigeot 19425718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); 19435718399fSFrançois Tigeot 19444dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 1945ba55f2f5SFrançois Tigeot drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 19465718399fSFrançois Tigeot 1947ba55f2f5SFrançois Tigeot connector = drm_connector_find(dev, out_resp->connector_id); 1948ba55f2f5SFrançois Tigeot if (!connector) { 19499edbd4a0SFrançois Tigeot ret = -ENOENT; 19505718399fSFrançois Tigeot goto out; 19515718399fSFrançois Tigeot } 19525718399fSFrançois Tigeot 1953b5162e19SFrançois Tigeot props_count = connector->properties.count; 19545718399fSFrançois Tigeot 19555718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 19565718399fSFrançois Tigeot if (connector->encoder_ids[i] != 0) { 19575718399fSFrançois Tigeot encoders_count++; 19585718399fSFrançois Tigeot } 19595718399fSFrançois Tigeot } 19605718399fSFrançois Tigeot 19615718399fSFrançois Tigeot if (out_resp->count_modes == 0) { 19625718399fSFrançois Tigeot connector->funcs->fill_modes(connector, 19635718399fSFrançois Tigeot dev->mode_config.max_width, 19645718399fSFrançois Tigeot dev->mode_config.max_height); 19655718399fSFrançois Tigeot } 19665718399fSFrançois Tigeot 19675718399fSFrançois Tigeot /* delayed so we get modes regardless of pre-fill_modes state */ 19685718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) 19699edbd4a0SFrançois Tigeot if (drm_mode_expose_to_userspace(mode, file_priv)) 19705718399fSFrançois Tigeot mode_count++; 19715718399fSFrançois Tigeot 19725718399fSFrançois Tigeot out_resp->connector_id = connector->base.id; 19735718399fSFrançois Tigeot out_resp->connector_type = connector->connector_type; 19745718399fSFrançois Tigeot out_resp->connector_type_id = connector->connector_type_id; 19755718399fSFrançois Tigeot out_resp->mm_width = connector->display_info.width_mm; 19765718399fSFrançois Tigeot out_resp->mm_height = connector->display_info.height_mm; 19775718399fSFrançois Tigeot out_resp->subpixel = connector->display_info.subpixel_order; 19785718399fSFrançois Tigeot out_resp->connection = connector->status; 19795718399fSFrançois Tigeot if (connector->encoder) 19805718399fSFrançois Tigeot out_resp->encoder_id = connector->encoder->base.id; 19815718399fSFrançois Tigeot else 19825718399fSFrançois Tigeot out_resp->encoder_id = 0; 19835718399fSFrançois Tigeot 19845718399fSFrançois Tigeot /* 19855718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 19865718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 19875718399fSFrançois Tigeot */ 19885718399fSFrançois Tigeot if ((out_resp->count_modes >= mode_count) && mode_count) { 19895718399fSFrançois Tigeot copied = 0; 1990b5162e19SFrançois Tigeot mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; 19915718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) { 19929edbd4a0SFrançois Tigeot if (!drm_mode_expose_to_userspace(mode, file_priv)) 19939edbd4a0SFrançois Tigeot continue; 19949edbd4a0SFrançois Tigeot 19955718399fSFrançois Tigeot drm_crtc_convert_to_umode(&u_mode, mode); 1996b5162e19SFrançois Tigeot if (copy_to_user(mode_ptr + copied, 1997b5162e19SFrançois Tigeot &u_mode, sizeof(u_mode))) { 1998b5162e19SFrançois Tigeot ret = -EFAULT; 19995718399fSFrançois Tigeot goto out; 20005718399fSFrançois Tigeot } 20015718399fSFrançois Tigeot copied++; 20025718399fSFrançois Tigeot } 20035718399fSFrançois Tigeot } 20045718399fSFrançois Tigeot out_resp->count_modes = mode_count; 20055718399fSFrançois Tigeot 20065718399fSFrançois Tigeot if ((out_resp->count_props >= props_count) && props_count) { 20075718399fSFrançois Tigeot copied = 0; 2008b5162e19SFrançois Tigeot prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); 2009b5162e19SFrançois Tigeot prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); 2010b5162e19SFrançois Tigeot for (i = 0; i < connector->properties.count; i++) { 2011b5162e19SFrançois Tigeot if (put_user(connector->properties.ids[i], 2012b5162e19SFrançois Tigeot prop_ptr + copied)) { 2013b5162e19SFrançois Tigeot ret = -EFAULT; 20145718399fSFrançois Tigeot goto out; 20155718399fSFrançois Tigeot } 20165718399fSFrançois Tigeot 2017b5162e19SFrançois Tigeot if (put_user(connector->properties.values[i], 2018b5162e19SFrançois Tigeot prop_values + copied)) { 2019b5162e19SFrançois Tigeot ret = -EFAULT; 20205718399fSFrançois Tigeot goto out; 20215718399fSFrançois Tigeot } 20225718399fSFrançois Tigeot copied++; 20235718399fSFrançois Tigeot } 20245718399fSFrançois Tigeot } 20255718399fSFrançois Tigeot out_resp->count_props = props_count; 20265718399fSFrançois Tigeot 20275718399fSFrançois Tigeot if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 20285718399fSFrançois Tigeot copied = 0; 2029b5162e19SFrançois Tigeot encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); 20305718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 20315718399fSFrançois Tigeot if (connector->encoder_ids[i] != 0) { 2032b5162e19SFrançois Tigeot if (put_user(connector->encoder_ids[i], 2033b5162e19SFrançois Tigeot encoder_ptr + copied)) { 2034b5162e19SFrançois Tigeot ret = -EFAULT; 20355718399fSFrançois Tigeot goto out; 20365718399fSFrançois Tigeot } 20375718399fSFrançois Tigeot copied++; 20385718399fSFrançois Tigeot } 20395718399fSFrançois Tigeot } 20405718399fSFrançois Tigeot } 20415718399fSFrançois Tigeot out_resp->count_encoders = encoders_count; 20425718399fSFrançois Tigeot 20435718399fSFrançois Tigeot out: 2044ba55f2f5SFrançois Tigeot drm_modeset_unlock(&dev->mode_config.connection_mutex); 20454dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 20464dbb207bSFrançois Tigeot 20475718399fSFrançois Tigeot return ret; 20485718399fSFrançois Tigeot } 20495718399fSFrançois Tigeot 2050ba55f2f5SFrançois Tigeot /** 2051ba55f2f5SFrançois Tigeot * drm_mode_getencoder - get encoder configuration 2052ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 2053ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 2054ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 2055ba55f2f5SFrançois Tigeot * 2056ba55f2f5SFrançois Tigeot * Construct a encoder configuration structure to return to the user. 2057ba55f2f5SFrançois Tigeot * 2058ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2059ba55f2f5SFrançois Tigeot * 2060ba55f2f5SFrançois Tigeot * Returns: 2061ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 2062ba55f2f5SFrançois Tigeot */ 20635718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data, 20645718399fSFrançois Tigeot struct drm_file *file_priv) 20655718399fSFrançois Tigeot { 20665718399fSFrançois Tigeot struct drm_mode_get_encoder *enc_resp = data; 20675718399fSFrançois Tigeot struct drm_encoder *encoder; 20685718399fSFrançois Tigeot int ret = 0; 20695718399fSFrançois Tigeot 20705718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2071b5162e19SFrançois Tigeot return -EINVAL; 20725718399fSFrançois Tigeot 20734dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 2074ba55f2f5SFrançois Tigeot encoder = drm_encoder_find(dev, enc_resp->encoder_id); 2075ba55f2f5SFrançois Tigeot if (!encoder) { 20769edbd4a0SFrançois Tigeot ret = -ENOENT; 20775718399fSFrançois Tigeot goto out; 20785718399fSFrançois Tigeot } 20795718399fSFrançois Tigeot 20805718399fSFrançois Tigeot if (encoder->crtc) 20815718399fSFrançois Tigeot enc_resp->crtc_id = encoder->crtc->base.id; 20825718399fSFrançois Tigeot else 20835718399fSFrançois Tigeot enc_resp->crtc_id = 0; 20845718399fSFrançois Tigeot enc_resp->encoder_type = encoder->encoder_type; 20855718399fSFrançois Tigeot enc_resp->encoder_id = encoder->base.id; 20865718399fSFrançois Tigeot enc_resp->possible_crtcs = encoder->possible_crtcs; 20875718399fSFrançois Tigeot enc_resp->possible_clones = encoder->possible_clones; 20885718399fSFrançois Tigeot 20895718399fSFrançois Tigeot out: 20904dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 20915718399fSFrançois Tigeot return ret; 20925718399fSFrançois Tigeot } 20935718399fSFrançois Tigeot 20945718399fSFrançois Tigeot /** 2095ba55f2f5SFrançois Tigeot * drm_mode_getplane_res - enumerate all plane resources 20965718399fSFrançois Tigeot * @dev: DRM device 20975718399fSFrançois Tigeot * @data: ioctl data 20985718399fSFrançois Tigeot * @file_priv: DRM file info 20995718399fSFrançois Tigeot * 2100ba55f2f5SFrançois Tigeot * Construct a list of plane ids to return to the user. 2101ba55f2f5SFrançois Tigeot * 2102ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2103ba55f2f5SFrançois Tigeot * 2104ba55f2f5SFrançois Tigeot * Returns: 2105ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 21065718399fSFrançois Tigeot */ 21075718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data, 21085718399fSFrançois Tigeot struct drm_file *file_priv) 21095718399fSFrançois Tigeot { 21105718399fSFrançois Tigeot struct drm_mode_get_plane_res *plane_resp = data; 21115718399fSFrançois Tigeot struct drm_mode_config *config; 21125718399fSFrançois Tigeot struct drm_plane *plane; 2113b5162e19SFrançois Tigeot uint32_t __user *plane_ptr; 21145718399fSFrançois Tigeot int copied = 0, ret = 0; 2115ba55f2f5SFrançois Tigeot unsigned num_planes; 21165718399fSFrançois Tigeot 21175718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2118b5162e19SFrançois Tigeot return -EINVAL; 21195718399fSFrançois Tigeot 21204dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 21215718399fSFrançois Tigeot config = &dev->mode_config; 21225718399fSFrançois Tigeot 2123ba55f2f5SFrançois Tigeot if (file_priv->universal_planes) 2124ba55f2f5SFrançois Tigeot num_planes = config->num_total_plane; 2125ba55f2f5SFrançois Tigeot else 2126ba55f2f5SFrançois Tigeot num_planes = config->num_overlay_plane; 2127ba55f2f5SFrançois Tigeot 21285718399fSFrançois Tigeot /* 21295718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 21305718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 21315718399fSFrançois Tigeot */ 2132ba55f2f5SFrançois Tigeot if (num_planes && 2133ba55f2f5SFrançois Tigeot (plane_resp->count_planes >= num_planes)) { 2134b5162e19SFrançois Tigeot plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; 21355718399fSFrançois Tigeot 21365718399fSFrançois Tigeot list_for_each_entry(plane, &config->plane_list, head) { 2137ba55f2f5SFrançois Tigeot /* 2138ba55f2f5SFrançois Tigeot * Unless userspace set the 'universal planes' 2139ba55f2f5SFrançois Tigeot * capability bit, only advertise overlays. 2140ba55f2f5SFrançois Tigeot */ 2141ba55f2f5SFrançois Tigeot if (plane->type != DRM_PLANE_TYPE_OVERLAY && 2142ba55f2f5SFrançois Tigeot !file_priv->universal_planes) 2143ba55f2f5SFrançois Tigeot continue; 2144ba55f2f5SFrançois Tigeot 2145b5162e19SFrançois Tigeot if (put_user(plane->base.id, plane_ptr + copied)) { 2146b5162e19SFrançois Tigeot ret = -EFAULT; 21475718399fSFrançois Tigeot goto out; 21485718399fSFrançois Tigeot } 21495718399fSFrançois Tigeot copied++; 21505718399fSFrançois Tigeot } 21515718399fSFrançois Tigeot } 2152ba55f2f5SFrançois Tigeot plane_resp->count_planes = num_planes; 21535718399fSFrançois Tigeot 21545718399fSFrançois Tigeot out: 21554dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 21565718399fSFrançois Tigeot return ret; 21575718399fSFrançois Tigeot } 21585718399fSFrançois Tigeot 21595718399fSFrançois Tigeot /** 2160ba55f2f5SFrançois Tigeot * drm_mode_getplane - get plane configuration 21615718399fSFrançois Tigeot * @dev: DRM device 21625718399fSFrançois Tigeot * @data: ioctl data 21635718399fSFrançois Tigeot * @file_priv: DRM file info 21645718399fSFrançois Tigeot * 2165ba55f2f5SFrançois Tigeot * Construct a plane configuration structure to return to the user. 2166ba55f2f5SFrançois Tigeot * 2167ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2168ba55f2f5SFrançois Tigeot * 2169ba55f2f5SFrançois Tigeot * Returns: 2170ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 21715718399fSFrançois Tigeot */ 21725718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data, 21735718399fSFrançois Tigeot struct drm_file *file_priv) 21745718399fSFrançois Tigeot { 21755718399fSFrançois Tigeot struct drm_mode_get_plane *plane_resp = data; 21765718399fSFrançois Tigeot struct drm_plane *plane; 2177b5162e19SFrançois Tigeot uint32_t __user *format_ptr; 21785718399fSFrançois Tigeot int ret = 0; 21795718399fSFrançois Tigeot 21805718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2181b5162e19SFrançois Tigeot return -EINVAL; 21825718399fSFrançois Tigeot 21834dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 2184ba55f2f5SFrançois Tigeot plane = drm_plane_find(dev, plane_resp->plane_id); 2185ba55f2f5SFrançois Tigeot if (!plane) { 2186b5162e19SFrançois Tigeot ret = -ENOENT; 21875718399fSFrançois Tigeot goto out; 21885718399fSFrançois Tigeot } 21895718399fSFrançois Tigeot 21905718399fSFrançois Tigeot if (plane->crtc) 21915718399fSFrançois Tigeot plane_resp->crtc_id = plane->crtc->base.id; 21925718399fSFrançois Tigeot else 21935718399fSFrançois Tigeot plane_resp->crtc_id = 0; 21945718399fSFrançois Tigeot 21955718399fSFrançois Tigeot if (plane->fb) 21965718399fSFrançois Tigeot plane_resp->fb_id = plane->fb->base.id; 21975718399fSFrançois Tigeot else 21985718399fSFrançois Tigeot plane_resp->fb_id = 0; 21995718399fSFrançois Tigeot 22005718399fSFrançois Tigeot plane_resp->plane_id = plane->base.id; 22015718399fSFrançois Tigeot plane_resp->possible_crtcs = plane->possible_crtcs; 220206fede5aSFrançois Tigeot plane_resp->gamma_size = 0; 22035718399fSFrançois Tigeot 22045718399fSFrançois Tigeot /* 22055718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 22065718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 22075718399fSFrançois Tigeot */ 22085718399fSFrançois Tigeot if (plane->format_count && 22095718399fSFrançois Tigeot (plane_resp->count_format_types >= plane->format_count)) { 2210b5162e19SFrançois Tigeot format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; 2211b5162e19SFrançois Tigeot if (copy_to_user(format_ptr, 22125718399fSFrançois Tigeot plane->format_types, 22135718399fSFrançois Tigeot sizeof(uint32_t) * plane->format_count)) { 2214b5162e19SFrançois Tigeot ret = -EFAULT; 22155718399fSFrançois Tigeot goto out; 22165718399fSFrançois Tigeot } 22175718399fSFrançois Tigeot } 22185718399fSFrançois Tigeot plane_resp->count_format_types = plane->format_count; 22195718399fSFrançois Tigeot 22205718399fSFrançois Tigeot out: 22214dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 22225718399fSFrançois Tigeot return ret; 22235718399fSFrançois Tigeot } 22245718399fSFrançois Tigeot 2225*24edb884SFrançois Tigeot /* 2226*24edb884SFrançois Tigeot * setplane_internal - setplane handler for internal callers 22275718399fSFrançois Tigeot * 2228*24edb884SFrançois Tigeot * Note that we assume an extra reference has already been taken on fb. If the 2229*24edb884SFrançois Tigeot * update fails, this reference will be dropped before return; if it succeeds, 2230*24edb884SFrançois Tigeot * the previous framebuffer (if any) will be unreferenced instead. 2231ba55f2f5SFrançois Tigeot * 2232*24edb884SFrançois Tigeot * src_{x,y,w,h} are provided in 16.16 fixed point format 22335718399fSFrançois Tigeot */ 2234*24edb884SFrançois Tigeot static int setplane_internal(struct drm_plane *plane, 2235*24edb884SFrançois Tigeot struct drm_crtc *crtc, 2236*24edb884SFrançois Tigeot struct drm_framebuffer *fb, 2237*24edb884SFrançois Tigeot int32_t crtc_x, int32_t crtc_y, 2238*24edb884SFrançois Tigeot uint32_t crtc_w, uint32_t crtc_h, 2239*24edb884SFrançois Tigeot /* src_{x,y,w,h} values are 16.16 fixed point */ 2240*24edb884SFrançois Tigeot uint32_t src_x, uint32_t src_y, 2241*24edb884SFrançois Tigeot uint32_t src_w, uint32_t src_h) 22425718399fSFrançois Tigeot { 2243*24edb884SFrançois Tigeot struct drm_device *dev = plane->dev; 2244*24edb884SFrançois Tigeot struct drm_framebuffer *old_fb = NULL; 22455718399fSFrançois Tigeot int ret = 0; 22465718399fSFrançois Tigeot unsigned int fb_width, fb_height; 22475718399fSFrançois Tigeot int i; 22485718399fSFrançois Tigeot 22495718399fSFrançois Tigeot /* No fb means shut it down */ 2250*24edb884SFrançois Tigeot if (!fb) { 22514dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 22524dbb207bSFrançois Tigeot old_fb = plane->fb; 2253ba55f2f5SFrançois Tigeot ret = plane->funcs->disable_plane(plane); 2254ba55f2f5SFrançois Tigeot if (!ret) { 22555718399fSFrançois Tigeot plane->crtc = NULL; 22565718399fSFrançois Tigeot plane->fb = NULL; 2257ba55f2f5SFrançois Tigeot } else { 2258ba55f2f5SFrançois Tigeot old_fb = NULL; 2259ba55f2f5SFrançois Tigeot } 22604dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 22615718399fSFrançois Tigeot goto out; 22625718399fSFrançois Tigeot } 22635718399fSFrançois Tigeot 2264*24edb884SFrançois Tigeot /* Check whether this plane is usable on this CRTC */ 2265*24edb884SFrançois Tigeot if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { 2266*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Invalid crtc for plane\n"); 2267*24edb884SFrançois Tigeot ret = -EINVAL; 22685718399fSFrançois Tigeot goto out; 22695718399fSFrançois Tigeot } 22705718399fSFrançois Tigeot 22715718399fSFrançois Tigeot /* Check whether this plane supports the fb pixel format. */ 22725718399fSFrançois Tigeot for (i = 0; i < plane->format_count; i++) 22735718399fSFrançois Tigeot if (fb->pixel_format == plane->format_types[i]) 22745718399fSFrançois Tigeot break; 22755718399fSFrançois Tigeot if (i == plane->format_count) { 227606fede5aSFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n", 227706fede5aSFrançois Tigeot drm_get_format_name(fb->pixel_format)); 2278b5162e19SFrançois Tigeot ret = -EINVAL; 22795718399fSFrançois Tigeot goto out; 22805718399fSFrançois Tigeot } 22815718399fSFrançois Tigeot 22825718399fSFrançois Tigeot fb_width = fb->width << 16; 22835718399fSFrançois Tigeot fb_height = fb->height << 16; 22845718399fSFrançois Tigeot 22855718399fSFrançois Tigeot /* Make sure source coordinates are inside the fb. */ 2286*24edb884SFrançois Tigeot if (src_w > fb_width || 2287*24edb884SFrançois Tigeot src_x > fb_width - src_w || 2288*24edb884SFrançois Tigeot src_h > fb_height || 2289*24edb884SFrançois Tigeot src_y > fb_height - src_h) { 22905718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid source coordinates " 22915718399fSFrançois Tigeot "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 2292*24edb884SFrançois Tigeot src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, 2293*24edb884SFrançois Tigeot src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, 2294*24edb884SFrançois Tigeot src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, 2295*24edb884SFrançois Tigeot src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); 2296b5162e19SFrançois Tigeot ret = -ENOSPC; 22975718399fSFrançois Tigeot goto out; 22985718399fSFrançois Tigeot } 22995718399fSFrançois Tigeot 23004dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 2301ba55f2f5SFrançois Tigeot old_fb = plane->fb; 2302b5162e19SFrançois Tigeot ret = plane->funcs->update_plane(plane, crtc, fb, 2303*24edb884SFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 2304*24edb884SFrançois Tigeot src_x, src_y, src_w, src_h); 23055718399fSFrançois Tigeot if (!ret) { 23065718399fSFrançois Tigeot plane->crtc = crtc; 23075718399fSFrançois Tigeot plane->fb = fb; 23084dbb207bSFrançois Tigeot fb = NULL; 2309ba55f2f5SFrançois Tigeot } else { 2310ba55f2f5SFrançois Tigeot old_fb = NULL; 23115718399fSFrançois Tigeot } 23124dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 23135718399fSFrançois Tigeot 23145718399fSFrançois Tigeot out: 23154dbb207bSFrançois Tigeot if (fb) 23164dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 23174dbb207bSFrançois Tigeot if (old_fb) 23184dbb207bSFrançois Tigeot drm_framebuffer_unreference(old_fb); 23195718399fSFrançois Tigeot 23205718399fSFrançois Tigeot return ret; 2321*24edb884SFrançois Tigeot 2322*24edb884SFrançois Tigeot } 2323*24edb884SFrançois Tigeot 2324*24edb884SFrançois Tigeot /** 2325*24edb884SFrançois Tigeot * drm_mode_setplane - configure a plane's configuration 2326*24edb884SFrançois Tigeot * @dev: DRM device 2327*24edb884SFrançois Tigeot * @data: ioctl data* 2328*24edb884SFrançois Tigeot * @file_priv: DRM file info 2329*24edb884SFrançois Tigeot * 2330*24edb884SFrançois Tigeot * Set plane configuration, including placement, fb, scaling, and other factors. 2331*24edb884SFrançois Tigeot * Or pass a NULL fb to disable (planes may be disabled without providing a 2332*24edb884SFrançois Tigeot * valid crtc). 2333*24edb884SFrançois Tigeot * 2334*24edb884SFrançois Tigeot * Returns: 2335*24edb884SFrançois Tigeot * Zero on success, errno on failure. 2336*24edb884SFrançois Tigeot */ 2337*24edb884SFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data, 2338*24edb884SFrançois Tigeot struct drm_file *file_priv) 2339*24edb884SFrançois Tigeot { 2340*24edb884SFrançois Tigeot struct drm_mode_set_plane *plane_req = data; 2341*24edb884SFrançois Tigeot struct drm_mode_object *obj; 2342*24edb884SFrançois Tigeot struct drm_plane *plane; 2343*24edb884SFrançois Tigeot struct drm_crtc *crtc = NULL; 2344*24edb884SFrançois Tigeot struct drm_framebuffer *fb = NULL; 2345*24edb884SFrançois Tigeot 2346*24edb884SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2347*24edb884SFrançois Tigeot return -EINVAL; 2348*24edb884SFrançois Tigeot 2349*24edb884SFrançois Tigeot /* Give drivers some help against integer overflows */ 2350*24edb884SFrançois Tigeot if (plane_req->crtc_w > INT_MAX || 2351*24edb884SFrançois Tigeot plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || 2352*24edb884SFrançois Tigeot plane_req->crtc_h > INT_MAX || 2353*24edb884SFrançois Tigeot plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { 2354*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 2355*24edb884SFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 2356*24edb884SFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y); 2357*24edb884SFrançois Tigeot return -ERANGE; 2358*24edb884SFrançois Tigeot } 2359*24edb884SFrançois Tigeot 2360*24edb884SFrançois Tigeot /* 2361*24edb884SFrançois Tigeot * First, find the plane, crtc, and fb objects. If not available, 2362*24edb884SFrançois Tigeot * we don't bother to call the driver. 2363*24edb884SFrançois Tigeot */ 2364*24edb884SFrançois Tigeot obj = drm_mode_object_find(dev, plane_req->plane_id, 2365*24edb884SFrançois Tigeot DRM_MODE_OBJECT_PLANE); 2366*24edb884SFrançois Tigeot if (!obj) { 2367*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown plane ID %d\n", 2368*24edb884SFrançois Tigeot plane_req->plane_id); 2369*24edb884SFrançois Tigeot return -ENOENT; 2370*24edb884SFrançois Tigeot } 2371*24edb884SFrançois Tigeot plane = obj_to_plane(obj); 2372*24edb884SFrançois Tigeot 2373*24edb884SFrançois Tigeot if (plane_req->fb_id) { 2374*24edb884SFrançois Tigeot fb = drm_framebuffer_lookup(dev, plane_req->fb_id); 2375*24edb884SFrançois Tigeot if (!fb) { 2376*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 2377*24edb884SFrançois Tigeot plane_req->fb_id); 2378*24edb884SFrançois Tigeot return -ENOENT; 2379*24edb884SFrançois Tigeot } 2380*24edb884SFrançois Tigeot 2381*24edb884SFrançois Tigeot obj = drm_mode_object_find(dev, plane_req->crtc_id, 2382*24edb884SFrançois Tigeot DRM_MODE_OBJECT_CRTC); 2383*24edb884SFrançois Tigeot if (!obj) { 2384*24edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown crtc ID %d\n", 2385*24edb884SFrançois Tigeot plane_req->crtc_id); 2386*24edb884SFrançois Tigeot return -ENOENT; 2387*24edb884SFrançois Tigeot } 2388*24edb884SFrançois Tigeot crtc = obj_to_crtc(obj); 2389*24edb884SFrançois Tigeot } 2390*24edb884SFrançois Tigeot 2391*24edb884SFrançois Tigeot /* 2392*24edb884SFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 2393*24edb884SFrançois Tigeot * framebuffer depending on success. 2394*24edb884SFrançois Tigeot */ 2395*24edb884SFrançois Tigeot return setplane_internal(plane, crtc, fb, 2396*24edb884SFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y, 2397*24edb884SFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 2398*24edb884SFrançois Tigeot plane_req->src_x, plane_req->src_y, 2399*24edb884SFrançois Tigeot plane_req->src_w, plane_req->src_h); 24005718399fSFrançois Tigeot } 24015718399fSFrançois Tigeot 24025718399fSFrançois Tigeot /** 24034dbb207bSFrançois Tigeot * drm_mode_set_config_internal - helper to call ->set_config 24044dbb207bSFrançois Tigeot * @set: modeset config to set 24055718399fSFrançois Tigeot * 24064dbb207bSFrançois Tigeot * This is a little helper to wrap internal calls to the ->set_config driver 24074dbb207bSFrançois Tigeot * interface. The only thing it adds is correct refcounting dance. 2408ba55f2f5SFrançois Tigeot * 2409ba55f2f5SFrançois Tigeot * Returns: 2410ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 24114dbb207bSFrançois Tigeot */ 24124dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set) 24134dbb207bSFrançois Tigeot { 24144dbb207bSFrançois Tigeot struct drm_crtc *crtc = set->crtc; 241506fede5aSFrançois Tigeot struct drm_framebuffer *fb; 241606fede5aSFrançois Tigeot struct drm_crtc *tmp; 24174dbb207bSFrançois Tigeot int ret; 24184dbb207bSFrançois Tigeot 241906fede5aSFrançois Tigeot /* 242006fede5aSFrançois Tigeot * NOTE: ->set_config can also disable other crtcs (if we steal all 242106fede5aSFrançois Tigeot * connectors from it), hence we need to refcount the fbs across all 242206fede5aSFrançois Tigeot * crtcs. Atomic modeset will have saner semantics ... 242306fede5aSFrançois Tigeot */ 242406fede5aSFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) 2425ba55f2f5SFrançois Tigeot tmp->old_fb = tmp->primary->fb; 242606fede5aSFrançois Tigeot 24274dbb207bSFrançois Tigeot fb = set->fb; 24284dbb207bSFrançois Tigeot 24294dbb207bSFrançois Tigeot ret = crtc->funcs->set_config(set); 24304dbb207bSFrançois Tigeot if (ret == 0) { 2431ba55f2f5SFrançois Tigeot crtc->primary->crtc = crtc; 2432ba55f2f5SFrançois Tigeot crtc->primary->fb = fb; 243306fede5aSFrançois Tigeot } 243406fede5aSFrançois Tigeot 243506fede5aSFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { 2436ba55f2f5SFrançois Tigeot if (tmp->primary->fb) 2437ba55f2f5SFrançois Tigeot drm_framebuffer_reference(tmp->primary->fb); 243806fede5aSFrançois Tigeot if (tmp->old_fb) 243906fede5aSFrançois Tigeot drm_framebuffer_unreference(tmp->old_fb); 24404dbb207bSFrançois Tigeot } 24414dbb207bSFrançois Tigeot 24424dbb207bSFrançois Tigeot return ret; 24434dbb207bSFrançois Tigeot } 24444dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal); 24454dbb207bSFrançois Tigeot 2446ba55f2f5SFrançois Tigeot /** 2447ba55f2f5SFrançois Tigeot * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the 2448ba55f2f5SFrançois Tigeot * CRTC viewport 2449ba55f2f5SFrançois Tigeot * @crtc: CRTC that framebuffer will be displayed on 2450ba55f2f5SFrançois Tigeot * @x: x panning 2451ba55f2f5SFrançois Tigeot * @y: y panning 2452ba55f2f5SFrançois Tigeot * @mode: mode that framebuffer will be displayed under 2453ba55f2f5SFrançois Tigeot * @fb: framebuffer to check size of 24549edbd4a0SFrançois Tigeot */ 2455ba55f2f5SFrançois Tigeot int drm_crtc_check_viewport(const struct drm_crtc *crtc, 24569edbd4a0SFrançois Tigeot int x, int y, 24579edbd4a0SFrançois Tigeot const struct drm_display_mode *mode, 24589edbd4a0SFrançois Tigeot const struct drm_framebuffer *fb) 24599edbd4a0SFrançois Tigeot 24609edbd4a0SFrançois Tigeot { 24619edbd4a0SFrançois Tigeot int hdisplay, vdisplay; 24629edbd4a0SFrançois Tigeot 24639edbd4a0SFrançois Tigeot hdisplay = mode->hdisplay; 24649edbd4a0SFrançois Tigeot vdisplay = mode->vdisplay; 24659edbd4a0SFrançois Tigeot 24669edbd4a0SFrançois Tigeot if (drm_mode_is_stereo(mode)) { 24679edbd4a0SFrançois Tigeot struct drm_display_mode adjusted = *mode; 24689edbd4a0SFrançois Tigeot 24699edbd4a0SFrançois Tigeot drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); 24709edbd4a0SFrançois Tigeot hdisplay = adjusted.crtc_hdisplay; 24719edbd4a0SFrançois Tigeot vdisplay = adjusted.crtc_vdisplay; 24729edbd4a0SFrançois Tigeot } 24739edbd4a0SFrançois Tigeot 24749edbd4a0SFrançois Tigeot if (crtc->invert_dimensions) 24759edbd4a0SFrançois Tigeot swap(hdisplay, vdisplay); 24769edbd4a0SFrançois Tigeot 24779edbd4a0SFrançois Tigeot if (hdisplay > fb->width || 24789edbd4a0SFrançois Tigeot vdisplay > fb->height || 24799edbd4a0SFrançois Tigeot x > fb->width - hdisplay || 24809edbd4a0SFrançois Tigeot y > fb->height - vdisplay) { 24819edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", 24829edbd4a0SFrançois Tigeot fb->width, fb->height, hdisplay, vdisplay, x, y, 24839edbd4a0SFrançois Tigeot crtc->invert_dimensions ? " (inverted)" : ""); 24849edbd4a0SFrançois Tigeot return -ENOSPC; 24859edbd4a0SFrançois Tigeot } 24869edbd4a0SFrançois Tigeot 24879edbd4a0SFrançois Tigeot return 0; 24889edbd4a0SFrançois Tigeot } 2489ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_check_viewport); 24909edbd4a0SFrançois Tigeot 24914dbb207bSFrançois Tigeot /** 24924dbb207bSFrançois Tigeot * drm_mode_setcrtc - set CRTC configuration 24934dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 24944dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 24954dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 24965718399fSFrançois Tigeot * 24975718399fSFrançois Tigeot * Build a new CRTC configuration based on user request. 24985718399fSFrançois Tigeot * 24995718399fSFrançois Tigeot * Called by the user via ioctl. 25005718399fSFrançois Tigeot * 2501ba55f2f5SFrançois Tigeot * Returns: 25025718399fSFrançois Tigeot * Zero on success, errno on failure. 25035718399fSFrançois Tigeot */ 25045718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data, 25055718399fSFrançois Tigeot struct drm_file *file_priv) 25065718399fSFrançois Tigeot { 25075718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 25085718399fSFrançois Tigeot struct drm_mode_crtc *crtc_req = data; 25095718399fSFrançois Tigeot struct drm_crtc *crtc; 25105718399fSFrançois Tigeot struct drm_connector **connector_set = NULL, *connector; 25115718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 25125718399fSFrançois Tigeot struct drm_display_mode *mode = NULL; 25135718399fSFrançois Tigeot struct drm_mode_set set; 2514b5162e19SFrançois Tigeot uint32_t __user *set_connectors_ptr; 2515b5162e19SFrançois Tigeot int ret; 25165718399fSFrançois Tigeot int i; 25175718399fSFrançois Tigeot 25185718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2519b5162e19SFrançois Tigeot return -EINVAL; 25205718399fSFrançois Tigeot 25215718399fSFrançois Tigeot /* For some reason crtc x/y offsets are signed internally. */ 25225718399fSFrançois Tigeot if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) 2523b5162e19SFrançois Tigeot return -ERANGE; 25245718399fSFrançois Tigeot 25254dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 2526ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_req->crtc_id); 2527ba55f2f5SFrançois Tigeot if (!crtc) { 25285718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 25299edbd4a0SFrançois Tigeot ret = -ENOENT; 25305718399fSFrançois Tigeot goto out; 25315718399fSFrançois Tigeot } 25325718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 25335718399fSFrançois Tigeot 25345718399fSFrançois Tigeot if (crtc_req->mode_valid) { 25355718399fSFrançois Tigeot /* If we have a mode we need a framebuffer. */ 25365718399fSFrançois Tigeot /* If we pass -1, set the mode with the currently bound fb */ 25375718399fSFrançois Tigeot if (crtc_req->fb_id == -1) { 2538ba55f2f5SFrançois Tigeot if (!crtc->primary->fb) { 25395718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 25405718399fSFrançois Tigeot ret = -EINVAL; 25415718399fSFrançois Tigeot goto out; 25425718399fSFrançois Tigeot } 2543ba55f2f5SFrançois Tigeot fb = crtc->primary->fb; 25444dbb207bSFrançois Tigeot /* Make refcounting symmetric with the lookup path. */ 25454dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 25465718399fSFrançois Tigeot } else { 25474dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); 25484dbb207bSFrançois Tigeot if (!fb) { 25495718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown FB ID%d\n", 25505718399fSFrançois Tigeot crtc_req->fb_id); 25519edbd4a0SFrançois Tigeot ret = -ENOENT; 25525718399fSFrançois Tigeot goto out; 25535718399fSFrançois Tigeot } 25545718399fSFrançois Tigeot } 25555718399fSFrançois Tigeot 25565718399fSFrançois Tigeot mode = drm_mode_create(dev); 25575718399fSFrançois Tigeot if (!mode) { 2558b5162e19SFrançois Tigeot ret = -ENOMEM; 25595718399fSFrançois Tigeot goto out; 25605718399fSFrançois Tigeot } 25615718399fSFrançois Tigeot 25625718399fSFrançois Tigeot ret = drm_crtc_convert_umode(mode, &crtc_req->mode); 25635718399fSFrançois Tigeot if (ret) { 25645718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid mode\n"); 25655718399fSFrançois Tigeot goto out; 25665718399fSFrançois Tigeot } 25675718399fSFrançois Tigeot 25685718399fSFrançois Tigeot drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 25695718399fSFrançois Tigeot 25709edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, 25719edbd4a0SFrançois Tigeot mode, fb); 25729edbd4a0SFrançois Tigeot if (ret) 25735718399fSFrançois Tigeot goto out; 25749edbd4a0SFrançois Tigeot 25755718399fSFrançois Tigeot } 25765718399fSFrançois Tigeot 25775718399fSFrançois Tigeot if (crtc_req->count_connectors == 0 && mode) { 25785718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 2579b5162e19SFrançois Tigeot ret = -EINVAL; 25805718399fSFrançois Tigeot goto out; 25815718399fSFrançois Tigeot } 25825718399fSFrançois Tigeot 25835718399fSFrançois Tigeot if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 25845718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 25855718399fSFrançois Tigeot crtc_req->count_connectors); 2586b5162e19SFrançois Tigeot ret = -EINVAL; 25875718399fSFrançois Tigeot goto out; 25885718399fSFrançois Tigeot } 25895718399fSFrançois Tigeot 25905718399fSFrançois Tigeot if (crtc_req->count_connectors > 0) { 25915718399fSFrançois Tigeot u32 out_id; 25925718399fSFrançois Tigeot 25935718399fSFrançois Tigeot /* Avoid unbounded kernel memory allocation */ 25945718399fSFrançois Tigeot if (crtc_req->count_connectors > config->num_connector) { 2595b5162e19SFrançois Tigeot ret = -EINVAL; 25965718399fSFrançois Tigeot goto out; 25975718399fSFrançois Tigeot } 25985718399fSFrançois Tigeot 25995718399fSFrançois Tigeot connector_set = kmalloc(crtc_req->count_connectors * 26004dbb207bSFrançois Tigeot sizeof(struct drm_connector *), 26014dbb207bSFrançois Tigeot M_DRM, M_WAITOK); 2602b5162e19SFrançois Tigeot if (!connector_set) { 2603b5162e19SFrançois Tigeot ret = -ENOMEM; 2604b5162e19SFrançois Tigeot goto out; 2605b5162e19SFrançois Tigeot } 26065718399fSFrançois Tigeot 26075718399fSFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) { 2608b5162e19SFrançois Tigeot set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; 2609b5162e19SFrançois Tigeot if (get_user(out_id, &set_connectors_ptr[i])) { 2610b5162e19SFrançois Tigeot ret = -EFAULT; 26115718399fSFrançois Tigeot goto out; 26125718399fSFrançois Tigeot } 26135718399fSFrançois Tigeot 2614ba55f2f5SFrançois Tigeot connector = drm_connector_find(dev, out_id); 2615ba55f2f5SFrançois Tigeot if (!connector) { 26165718399fSFrançois Tigeot DRM_DEBUG_KMS("Connector id %d unknown\n", 26175718399fSFrançois Tigeot out_id); 26189edbd4a0SFrançois Tigeot ret = -ENOENT; 26195718399fSFrançois Tigeot goto out; 26205718399fSFrançois Tigeot } 26215718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 26225718399fSFrançois Tigeot connector->base.id, 2623ba55f2f5SFrançois Tigeot connector->name); 26245718399fSFrançois Tigeot 26255718399fSFrançois Tigeot connector_set[i] = connector; 26265718399fSFrançois Tigeot } 26275718399fSFrançois Tigeot } 26285718399fSFrançois Tigeot 26295718399fSFrançois Tigeot set.crtc = crtc; 26305718399fSFrançois Tigeot set.x = crtc_req->x; 26315718399fSFrançois Tigeot set.y = crtc_req->y; 26325718399fSFrançois Tigeot set.mode = mode; 26335718399fSFrançois Tigeot set.connectors = connector_set; 26345718399fSFrançois Tigeot set.num_connectors = crtc_req->count_connectors; 26355718399fSFrançois Tigeot set.fb = fb; 26364dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 26375718399fSFrançois Tigeot 26385718399fSFrançois Tigeot out: 26394dbb207bSFrançois Tigeot if (fb) 26404dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 26414dbb207bSFrançois Tigeot 26424dbb207bSFrançois Tigeot kfree(connector_set); 26435718399fSFrançois Tigeot drm_mode_destroy(dev, mode); 26444dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 26455718399fSFrançois Tigeot return ret; 26465718399fSFrançois Tigeot } 26475718399fSFrançois Tigeot 2648*24edb884SFrançois Tigeot /** 2649*24edb884SFrançois Tigeot * drm_mode_cursor_universal - translate legacy cursor ioctl call into a 2650*24edb884SFrançois Tigeot * universal plane handler call 2651*24edb884SFrançois Tigeot * @crtc: crtc to update cursor for 2652*24edb884SFrançois Tigeot * @req: data pointer for the ioctl 2653*24edb884SFrançois Tigeot * @file_priv: drm file for the ioctl call 2654*24edb884SFrançois Tigeot * 2655*24edb884SFrançois Tigeot * Legacy cursor ioctl's work directly with driver buffer handles. To 2656*24edb884SFrançois Tigeot * translate legacy ioctl calls into universal plane handler calls, we need to 2657*24edb884SFrançois Tigeot * wrap the native buffer handle in a drm_framebuffer. 2658*24edb884SFrançois Tigeot * 2659*24edb884SFrançois Tigeot * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB 2660*24edb884SFrançois Tigeot * buffer with a pitch of 4*width; the universal plane interface should be used 2661*24edb884SFrançois Tigeot * directly in cases where the hardware can support other buffer settings and 2662*24edb884SFrançois Tigeot * userspace wants to make use of these capabilities. 2663*24edb884SFrançois Tigeot * 2664*24edb884SFrançois Tigeot * Returns: 2665*24edb884SFrançois Tigeot * Zero on success, errno on failure. 2666*24edb884SFrançois Tigeot */ 2667*24edb884SFrançois Tigeot static int drm_mode_cursor_universal(struct drm_crtc *crtc, 2668*24edb884SFrançois Tigeot struct drm_mode_cursor2 *req, 2669*24edb884SFrançois Tigeot struct drm_file *file_priv) 2670*24edb884SFrançois Tigeot { 2671*24edb884SFrançois Tigeot struct drm_device *dev = crtc->dev; 2672*24edb884SFrançois Tigeot struct drm_framebuffer *fb = NULL; 2673*24edb884SFrançois Tigeot struct drm_mode_fb_cmd2 fbreq = { 2674*24edb884SFrançois Tigeot .width = req->width, 2675*24edb884SFrançois Tigeot .height = req->height, 2676*24edb884SFrançois Tigeot .pixel_format = DRM_FORMAT_ARGB8888, 2677*24edb884SFrançois Tigeot .pitches = { req->width * 4 }, 2678*24edb884SFrançois Tigeot .handles = { req->handle }, 2679*24edb884SFrançois Tigeot }; 2680*24edb884SFrançois Tigeot int32_t crtc_x, crtc_y; 2681*24edb884SFrançois Tigeot uint32_t crtc_w = 0, crtc_h = 0; 2682*24edb884SFrançois Tigeot uint32_t src_w = 0, src_h = 0; 2683*24edb884SFrançois Tigeot int ret = 0; 2684*24edb884SFrançois Tigeot 2685*24edb884SFrançois Tigeot BUG_ON(!crtc->cursor); 2686*24edb884SFrançois Tigeot 2687*24edb884SFrançois Tigeot /* 2688*24edb884SFrançois Tigeot * Obtain fb we'll be using (either new or existing) and take an extra 2689*24edb884SFrançois Tigeot * reference to it if fb != null. setplane will take care of dropping 2690*24edb884SFrançois Tigeot * the reference if the plane update fails. 2691*24edb884SFrançois Tigeot */ 2692*24edb884SFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 2693*24edb884SFrançois Tigeot if (req->handle) { 2694*24edb884SFrançois Tigeot fb = add_framebuffer_internal(dev, &fbreq, file_priv); 2695*24edb884SFrançois Tigeot if (IS_ERR(fb)) { 2696*24edb884SFrançois Tigeot DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); 2697*24edb884SFrançois Tigeot return PTR_ERR(fb); 2698*24edb884SFrançois Tigeot } 2699*24edb884SFrançois Tigeot 2700*24edb884SFrançois Tigeot drm_framebuffer_reference(fb); 2701*24edb884SFrançois Tigeot } else { 2702*24edb884SFrançois Tigeot fb = NULL; 2703*24edb884SFrançois Tigeot } 2704*24edb884SFrançois Tigeot } else { 2705*24edb884SFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 2706*24edb884SFrançois Tigeot fb = crtc->cursor->fb; 2707*24edb884SFrançois Tigeot if (fb) 2708*24edb884SFrançois Tigeot drm_framebuffer_reference(fb); 2709*24edb884SFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 2710*24edb884SFrançois Tigeot } 2711*24edb884SFrançois Tigeot 2712*24edb884SFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 2713*24edb884SFrançois Tigeot crtc_x = req->x; 2714*24edb884SFrançois Tigeot crtc_y = req->y; 2715*24edb884SFrançois Tigeot } else { 2716*24edb884SFrançois Tigeot crtc_x = crtc->cursor_x; 2717*24edb884SFrançois Tigeot crtc_y = crtc->cursor_y; 2718*24edb884SFrançois Tigeot } 2719*24edb884SFrançois Tigeot 2720*24edb884SFrançois Tigeot if (fb) { 2721*24edb884SFrançois Tigeot crtc_w = fb->width; 2722*24edb884SFrançois Tigeot crtc_h = fb->height; 2723*24edb884SFrançois Tigeot src_w = fb->width << 16; 2724*24edb884SFrançois Tigeot src_h = fb->height << 16; 2725*24edb884SFrançois Tigeot } 2726*24edb884SFrançois Tigeot 2727*24edb884SFrançois Tigeot /* 2728*24edb884SFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 2729*24edb884SFrançois Tigeot * framebuffer depending on success. 2730*24edb884SFrançois Tigeot */ 2731*24edb884SFrançois Tigeot ret = setplane_internal(crtc->cursor, crtc, fb, 2732*24edb884SFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 2733*24edb884SFrançois Tigeot 0, 0, src_w, src_h); 2734*24edb884SFrançois Tigeot 2735*24edb884SFrançois Tigeot /* Update successful; save new cursor position, if necessary */ 2736*24edb884SFrançois Tigeot if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { 2737*24edb884SFrançois Tigeot crtc->cursor_x = req->x; 2738*24edb884SFrançois Tigeot crtc->cursor_y = req->y; 2739*24edb884SFrançois Tigeot } 2740*24edb884SFrançois Tigeot 2741*24edb884SFrançois Tigeot return ret; 2742*24edb884SFrançois Tigeot } 2743*24edb884SFrançois Tigeot 274406fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev, 274506fede5aSFrançois Tigeot struct drm_mode_cursor2 *req, 274606fede5aSFrançois Tigeot struct drm_file *file_priv) 27475718399fSFrançois Tigeot { 27485718399fSFrançois Tigeot struct drm_crtc *crtc; 27495718399fSFrançois Tigeot int ret = 0; 27505718399fSFrançois Tigeot 27515718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2752b5162e19SFrançois Tigeot return -EINVAL; 27535718399fSFrançois Tigeot 2754b5162e19SFrançois Tigeot if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) 2755b5162e19SFrançois Tigeot return -EINVAL; 27565718399fSFrançois Tigeot 2757ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, req->crtc_id); 2758ba55f2f5SFrançois Tigeot if (!crtc) { 27595718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 27609edbd4a0SFrançois Tigeot return -ENOENT; 27615718399fSFrançois Tigeot } 27625718399fSFrançois Tigeot 2763*24edb884SFrançois Tigeot /* 2764*24edb884SFrançois Tigeot * If this crtc has a universal cursor plane, call that plane's update 2765*24edb884SFrançois Tigeot * handler rather than using legacy cursor handlers. 2766*24edb884SFrançois Tigeot */ 2767*24edb884SFrançois Tigeot if (crtc->cursor) 2768*24edb884SFrançois Tigeot return drm_mode_cursor_universal(crtc, req, file_priv); 2769*24edb884SFrançois Tigeot 2770ba55f2f5SFrançois Tigeot drm_modeset_lock(&crtc->mutex, NULL); 27715718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 277206fede5aSFrançois Tigeot if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { 2773b5162e19SFrançois Tigeot ret = -ENXIO; 27745718399fSFrançois Tigeot goto out; 27755718399fSFrançois Tigeot } 27765718399fSFrançois Tigeot /* Turns off the cursor if handle is 0 */ 277706fede5aSFrançois Tigeot if (crtc->funcs->cursor_set2) 277806fede5aSFrançois Tigeot ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, 277906fede5aSFrançois Tigeot req->width, req->height, req->hot_x, req->hot_y); 278006fede5aSFrançois Tigeot else 2781b5162e19SFrançois Tigeot ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, 27825718399fSFrançois Tigeot req->width, req->height); 27835718399fSFrançois Tigeot } 27845718399fSFrançois Tigeot 27855718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 27865718399fSFrançois Tigeot if (crtc->funcs->cursor_move) { 27875718399fSFrançois Tigeot ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 27885718399fSFrançois Tigeot } else { 2789b5162e19SFrançois Tigeot ret = -EFAULT; 27905718399fSFrançois Tigeot goto out; 27915718399fSFrançois Tigeot } 27925718399fSFrançois Tigeot } 27935718399fSFrançois Tigeot out: 2794ba55f2f5SFrançois Tigeot drm_modeset_unlock(&crtc->mutex); 27954dbb207bSFrançois Tigeot 27965718399fSFrançois Tigeot return ret; 279706fede5aSFrançois Tigeot 279806fede5aSFrançois Tigeot } 2799ba55f2f5SFrançois Tigeot 2800ba55f2f5SFrançois Tigeot 2801ba55f2f5SFrançois Tigeot /** 2802ba55f2f5SFrançois Tigeot * drm_mode_cursor_ioctl - set CRTC's cursor configuration 2803ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 2804ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 2805ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 2806ba55f2f5SFrançois Tigeot * 2807ba55f2f5SFrançois Tigeot * Set the cursor configuration based on user request. 2808ba55f2f5SFrançois Tigeot * 2809ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2810ba55f2f5SFrançois Tigeot * 2811ba55f2f5SFrançois Tigeot * Returns: 2812ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 2813ba55f2f5SFrançois Tigeot */ 281406fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev, 281506fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 281606fede5aSFrançois Tigeot { 281706fede5aSFrançois Tigeot struct drm_mode_cursor *req = data; 281806fede5aSFrançois Tigeot struct drm_mode_cursor2 new_req; 281906fede5aSFrançois Tigeot 282006fede5aSFrançois Tigeot memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); 282106fede5aSFrançois Tigeot new_req.hot_x = new_req.hot_y = 0; 282206fede5aSFrançois Tigeot 282306fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, &new_req, file_priv); 282406fede5aSFrançois Tigeot } 282506fede5aSFrançois Tigeot 2826ba55f2f5SFrançois Tigeot /** 2827ba55f2f5SFrançois Tigeot * drm_mode_cursor2_ioctl - set CRTC's cursor configuration 2828ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 2829ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 2830ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 2831ba55f2f5SFrançois Tigeot * 2832ba55f2f5SFrançois Tigeot * Set the cursor configuration based on user request. This implements the 2nd 2833ba55f2f5SFrançois Tigeot * version of the cursor ioctl, which allows userspace to additionally specify 2834ba55f2f5SFrançois Tigeot * the hotspot of the pointer. 2835ba55f2f5SFrançois Tigeot * 2836ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2837ba55f2f5SFrançois Tigeot * 2838ba55f2f5SFrançois Tigeot * Returns: 2839ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 2840ba55f2f5SFrançois Tigeot */ 284106fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev, 284206fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 284306fede5aSFrançois Tigeot { 284406fede5aSFrançois Tigeot struct drm_mode_cursor2 *req = data; 284506fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, req, file_priv); 28465718399fSFrançois Tigeot } 28475718399fSFrançois Tigeot 2848ba55f2f5SFrançois Tigeot /** 2849ba55f2f5SFrançois Tigeot * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description 2850ba55f2f5SFrançois Tigeot * @bpp: bits per pixels 2851ba55f2f5SFrançois Tigeot * @depth: bit depth per pixel 2852ba55f2f5SFrançois Tigeot * 2853ba55f2f5SFrançois Tigeot * Computes a drm fourcc pixel format code for the given @bpp/@depth values. 2854ba55f2f5SFrançois Tigeot * Useful in fbdev emulation code, since that deals in those values. 2855ba55f2f5SFrançois Tigeot */ 28565718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) 28575718399fSFrançois Tigeot { 28585718399fSFrançois Tigeot uint32_t fmt; 28595718399fSFrançois Tigeot 28605718399fSFrançois Tigeot switch (bpp) { 28615718399fSFrançois Tigeot case 8: 28624dbb207bSFrançois Tigeot fmt = DRM_FORMAT_C8; 28635718399fSFrançois Tigeot break; 28645718399fSFrançois Tigeot case 16: 28655718399fSFrançois Tigeot if (depth == 15) 28665718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB1555; 28675718399fSFrançois Tigeot else 28685718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB565; 28695718399fSFrançois Tigeot break; 28705718399fSFrançois Tigeot case 24: 28715718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB888; 28725718399fSFrançois Tigeot break; 28735718399fSFrançois Tigeot case 32: 28745718399fSFrançois Tigeot if (depth == 24) 28755718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 28765718399fSFrançois Tigeot else if (depth == 30) 28775718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB2101010; 28785718399fSFrançois Tigeot else 28795718399fSFrançois Tigeot fmt = DRM_FORMAT_ARGB8888; 28805718399fSFrançois Tigeot break; 28815718399fSFrançois Tigeot default: 2882b5162e19SFrançois Tigeot DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); 28835718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 28845718399fSFrançois Tigeot break; 28855718399fSFrançois Tigeot } 28865718399fSFrançois Tigeot 28875718399fSFrançois Tigeot return fmt; 28885718399fSFrançois Tigeot } 2889b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format); 28905718399fSFrançois Tigeot 28915718399fSFrançois Tigeot /** 28925718399fSFrançois Tigeot * drm_mode_addfb - add an FB to the graphics configuration 28934dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 28944dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 28954dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 28965718399fSFrançois Tigeot * 2897ba55f2f5SFrançois Tigeot * Add a new FB to the specified CRTC, given a user request. This is the 2898ba55f2f5SFrançois Tigeot * original addfb ioclt which only supported RGB formats. 28995718399fSFrançois Tigeot * 29005718399fSFrançois Tigeot * Called by the user via ioctl. 29015718399fSFrançois Tigeot * 2902ba55f2f5SFrançois Tigeot * Returns: 29035718399fSFrançois Tigeot * Zero on success, errno on failure. 29045718399fSFrançois Tigeot */ 29055718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev, 29065718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 29075718399fSFrançois Tigeot { 29085718399fSFrançois Tigeot struct drm_mode_fb_cmd *or = data; 29095718399fSFrançois Tigeot struct drm_mode_fb_cmd2 r = {}; 29105718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 29115718399fSFrançois Tigeot struct drm_framebuffer *fb; 29125718399fSFrançois Tigeot int ret = 0; 29135718399fSFrançois Tigeot 29145718399fSFrançois Tigeot /* Use new struct with format internally */ 29155718399fSFrançois Tigeot r.fb_id = or->fb_id; 29165718399fSFrançois Tigeot r.width = or->width; 29175718399fSFrançois Tigeot r.height = or->height; 29185718399fSFrançois Tigeot r.pitches[0] = or->pitch; 29195718399fSFrançois Tigeot r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 29205718399fSFrançois Tigeot r.handles[0] = or->handle; 29215718399fSFrançois Tigeot 29225718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2923b5162e19SFrançois Tigeot return -EINVAL; 29245718399fSFrançois Tigeot 29255718399fSFrançois Tigeot if ((config->min_width > r.width) || (r.width > config->max_width)) 2926b5162e19SFrançois Tigeot return -EINVAL; 2927b5162e19SFrançois Tigeot 29285718399fSFrançois Tigeot if ((config->min_height > r.height) || (r.height > config->max_height)) 2929b5162e19SFrançois Tigeot return -EINVAL; 29305718399fSFrançois Tigeot 293160fc7eecSFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); 293260fc7eecSFrançois Tigeot if (IS_ERR(fb)) { 293360fc7eecSFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 29344dbb207bSFrançois Tigeot return PTR_ERR(fb); 29355718399fSFrançois Tigeot } 29365718399fSFrançois Tigeot 29374dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 29385718399fSFrançois Tigeot or->fb_id = fb->base.id; 29395718399fSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 29405718399fSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 29414dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 29425718399fSFrançois Tigeot 29435718399fSFrançois Tigeot return ret; 29445718399fSFrançois Tigeot } 29455718399fSFrançois Tigeot 2946b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r) 29475718399fSFrançois Tigeot { 29485718399fSFrançois Tigeot uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 29495718399fSFrançois Tigeot 29505718399fSFrançois Tigeot switch (format) { 29515718399fSFrançois Tigeot case DRM_FORMAT_C8: 29525718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 29535718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 29545718399fSFrançois Tigeot case DRM_FORMAT_XRGB4444: 29555718399fSFrançois Tigeot case DRM_FORMAT_XBGR4444: 29565718399fSFrançois Tigeot case DRM_FORMAT_RGBX4444: 29575718399fSFrançois Tigeot case DRM_FORMAT_BGRX4444: 29585718399fSFrançois Tigeot case DRM_FORMAT_ARGB4444: 29595718399fSFrançois Tigeot case DRM_FORMAT_ABGR4444: 29605718399fSFrançois Tigeot case DRM_FORMAT_RGBA4444: 29615718399fSFrançois Tigeot case DRM_FORMAT_BGRA4444: 29625718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 29635718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 29645718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 29655718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 29665718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 29675718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 29685718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 29695718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 29705718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 29715718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 29725718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 29735718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 29745718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 29755718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 29765718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 29775718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 29785718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 29795718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 29805718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 29815718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 29825718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 29835718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 29845718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 29855718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 29865718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 29875718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 29885718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 29895718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 29905718399fSFrançois Tigeot case DRM_FORMAT_YUYV: 29915718399fSFrançois Tigeot case DRM_FORMAT_YVYU: 29925718399fSFrançois Tigeot case DRM_FORMAT_UYVY: 29935718399fSFrançois Tigeot case DRM_FORMAT_VYUY: 29945718399fSFrançois Tigeot case DRM_FORMAT_AYUV: 29955718399fSFrançois Tigeot case DRM_FORMAT_NV12: 29965718399fSFrançois Tigeot case DRM_FORMAT_NV21: 29975718399fSFrançois Tigeot case DRM_FORMAT_NV16: 29985718399fSFrançois Tigeot case DRM_FORMAT_NV61: 2999b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 3000b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 30015718399fSFrançois Tigeot case DRM_FORMAT_YUV410: 30025718399fSFrançois Tigeot case DRM_FORMAT_YVU410: 30035718399fSFrançois Tigeot case DRM_FORMAT_YUV411: 30045718399fSFrançois Tigeot case DRM_FORMAT_YVU411: 30055718399fSFrançois Tigeot case DRM_FORMAT_YUV420: 30065718399fSFrançois Tigeot case DRM_FORMAT_YVU420: 30075718399fSFrançois Tigeot case DRM_FORMAT_YUV422: 30085718399fSFrançois Tigeot case DRM_FORMAT_YVU422: 30095718399fSFrançois Tigeot case DRM_FORMAT_YUV444: 30105718399fSFrançois Tigeot case DRM_FORMAT_YVU444: 30115718399fSFrançois Tigeot return 0; 30125718399fSFrançois Tigeot default: 30139edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("invalid pixel format %s\n", 30149edbd4a0SFrançois Tigeot drm_get_format_name(r->pixel_format)); 3015b5162e19SFrançois Tigeot return -EINVAL; 30165718399fSFrançois Tigeot } 30175718399fSFrançois Tigeot } 30185718399fSFrançois Tigeot 3019b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) 3020b5162e19SFrançois Tigeot { 3021b5162e19SFrançois Tigeot int ret, hsub, vsub, num_planes, i; 3022b5162e19SFrançois Tigeot 3023b5162e19SFrançois Tigeot ret = format_check(r); 3024b5162e19SFrançois Tigeot if (ret) { 302506fede5aSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer format %s\n", 302606fede5aSFrançois Tigeot drm_get_format_name(r->pixel_format)); 3027b5162e19SFrançois Tigeot return ret; 3028b5162e19SFrançois Tigeot } 3029b5162e19SFrançois Tigeot 3030b5162e19SFrançois Tigeot hsub = drm_format_horz_chroma_subsampling(r->pixel_format); 3031b5162e19SFrançois Tigeot vsub = drm_format_vert_chroma_subsampling(r->pixel_format); 3032b5162e19SFrançois Tigeot num_planes = drm_format_num_planes(r->pixel_format); 3033b5162e19SFrançois Tigeot 3034b5162e19SFrançois Tigeot if (r->width == 0 || r->width % hsub) { 3035b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); 3036b5162e19SFrançois Tigeot return -EINVAL; 3037b5162e19SFrançois Tigeot } 3038b5162e19SFrançois Tigeot 3039b5162e19SFrançois Tigeot if (r->height == 0 || r->height % vsub) { 3040b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); 3041b5162e19SFrançois Tigeot return -EINVAL; 3042b5162e19SFrançois Tigeot } 3043b5162e19SFrançois Tigeot 3044b5162e19SFrançois Tigeot for (i = 0; i < num_planes; i++) { 3045b5162e19SFrançois Tigeot unsigned int width = r->width / (i != 0 ? hsub : 1); 3046b5162e19SFrançois Tigeot unsigned int height = r->height / (i != 0 ? vsub : 1); 3047b5162e19SFrançois Tigeot unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); 3048b5162e19SFrançois Tigeot 3049b5162e19SFrançois Tigeot if (!r->handles[i]) { 3050b5162e19SFrançois Tigeot DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); 3051b5162e19SFrançois Tigeot return -EINVAL; 3052b5162e19SFrançois Tigeot } 3053b5162e19SFrançois Tigeot 3054b5162e19SFrançois Tigeot if ((uint64_t) width * cpp > UINT_MAX) 3055b5162e19SFrançois Tigeot return -ERANGE; 3056b5162e19SFrançois Tigeot 3057b5162e19SFrançois Tigeot if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 3058b5162e19SFrançois Tigeot return -ERANGE; 3059b5162e19SFrançois Tigeot 3060b5162e19SFrançois Tigeot if (r->pitches[i] < width * cpp) { 3061b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); 3062b5162e19SFrançois Tigeot return -EINVAL; 3063b5162e19SFrançois Tigeot } 3064b5162e19SFrançois Tigeot } 3065b5162e19SFrançois Tigeot 3066b5162e19SFrançois Tigeot return 0; 3067b5162e19SFrançois Tigeot } 3068b5162e19SFrançois Tigeot 3069*24edb884SFrançois Tigeot static struct drm_framebuffer *add_framebuffer_internal(struct drm_device *dev, 3070*24edb884SFrançois Tigeot struct drm_mode_fb_cmd2 *r, 3071*24edb884SFrançois Tigeot struct drm_file *file_priv) 3072*24edb884SFrançois Tigeot { 3073*24edb884SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 3074*24edb884SFrançois Tigeot struct drm_framebuffer *fb; 3075*24edb884SFrançois Tigeot int ret; 3076*24edb884SFrançois Tigeot 3077*24edb884SFrançois Tigeot if (r->flags & ~DRM_MODE_FB_INTERLACED) { 3078*24edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); 3079*24edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 3080*24edb884SFrançois Tigeot } 3081*24edb884SFrançois Tigeot 3082*24edb884SFrançois Tigeot if ((config->min_width > r->width) || (r->width > config->max_width)) { 3083*24edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", 3084*24edb884SFrançois Tigeot r->width, config->min_width, config->max_width); 3085*24edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 3086*24edb884SFrançois Tigeot } 3087*24edb884SFrançois Tigeot if ((config->min_height > r->height) || (r->height > config->max_height)) { 3088*24edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", 3089*24edb884SFrançois Tigeot r->height, config->min_height, config->max_height); 3090*24edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 3091*24edb884SFrançois Tigeot } 3092*24edb884SFrançois Tigeot 3093*24edb884SFrançois Tigeot ret = framebuffer_check(r); 3094*24edb884SFrançois Tigeot if (ret) 3095*24edb884SFrançois Tigeot return ERR_PTR(ret); 3096*24edb884SFrançois Tigeot 3097*24edb884SFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 3098*24edb884SFrançois Tigeot if (IS_ERR(fb)) { 3099*24edb884SFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 3100*24edb884SFrançois Tigeot return fb; 3101*24edb884SFrançois Tigeot } 3102*24edb884SFrançois Tigeot 3103*24edb884SFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 3104*24edb884SFrançois Tigeot r->fb_id = fb->base.id; 3105*24edb884SFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 3106*24edb884SFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 3107*24edb884SFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 3108*24edb884SFrançois Tigeot 3109*24edb884SFrançois Tigeot return fb; 3110*24edb884SFrançois Tigeot } 3111*24edb884SFrançois Tigeot 31125718399fSFrançois Tigeot /** 31135718399fSFrançois Tigeot * drm_mode_addfb2 - add an FB to the graphics configuration 31144dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 31154dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 31164dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 31175718399fSFrançois Tigeot * 3118ba55f2f5SFrançois Tigeot * Add a new FB to the specified CRTC, given a user request with format. This is 3119ba55f2f5SFrançois Tigeot * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers 3120ba55f2f5SFrançois Tigeot * and uses fourcc codes as pixel format specifiers. 31215718399fSFrançois Tigeot * 31225718399fSFrançois Tigeot * Called by the user via ioctl. 31235718399fSFrançois Tigeot * 3124ba55f2f5SFrançois Tigeot * Returns: 31255718399fSFrançois Tigeot * Zero on success, errno on failure. 31265718399fSFrançois Tigeot */ 31275718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev, 31285718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 31295718399fSFrançois Tigeot { 31305718399fSFrançois Tigeot struct drm_framebuffer *fb; 31315718399fSFrançois Tigeot 31325718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3133b5162e19SFrançois Tigeot return -EINVAL; 3134b5162e19SFrançois Tigeot 3135*24edb884SFrançois Tigeot fb = add_framebuffer_internal(dev, data, file_priv); 3136*24edb884SFrançois Tigeot if (IS_ERR(fb)) 31374dbb207bSFrançois Tigeot return PTR_ERR(fb); 31385718399fSFrançois Tigeot 3139*24edb884SFrançois Tigeot return 0; 31405718399fSFrançois Tigeot } 31415718399fSFrançois Tigeot 31425718399fSFrançois Tigeot /** 31435718399fSFrançois Tigeot * drm_mode_rmfb - remove an FB from the configuration 31444dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 31454dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 31464dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 31475718399fSFrançois Tigeot * 31485718399fSFrançois Tigeot * Remove the FB specified by the user. 31495718399fSFrançois Tigeot * 31505718399fSFrançois Tigeot * Called by the user via ioctl. 31515718399fSFrançois Tigeot * 3152ba55f2f5SFrançois Tigeot * Returns: 31535718399fSFrançois Tigeot * Zero on success, errno on failure. 31545718399fSFrançois Tigeot */ 31555718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev, 31565718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 31575718399fSFrançois Tigeot { 31585718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 31595718399fSFrançois Tigeot struct drm_framebuffer *fbl = NULL; 31605718399fSFrançois Tigeot uint32_t *id = data; 31615718399fSFrançois Tigeot int found = 0; 31625718399fSFrançois Tigeot 31635718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3164b5162e19SFrançois Tigeot return -EINVAL; 31655718399fSFrançois Tigeot 31664dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 31674dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 31684dbb207bSFrançois Tigeot fb = __drm_framebuffer_lookup(dev, *id); 31694dbb207bSFrançois Tigeot if (!fb) 31704dbb207bSFrançois Tigeot goto fail_lookup; 31715718399fSFrançois Tigeot 31725718399fSFrançois Tigeot list_for_each_entry(fbl, &file_priv->fbs, filp_head) 31735718399fSFrançois Tigeot if (fb == fbl) 31745718399fSFrançois Tigeot found = 1; 31754dbb207bSFrançois Tigeot if (!found) 31764dbb207bSFrançois Tigeot goto fail_lookup; 31775718399fSFrançois Tigeot 31784dbb207bSFrançois Tigeot /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ 31794dbb207bSFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 31804dbb207bSFrançois Tigeot 31814dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 31824dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 31834dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 31845718399fSFrançois Tigeot 3185b5162e19SFrançois Tigeot drm_framebuffer_remove(fb); 31865718399fSFrançois Tigeot 31874dbb207bSFrançois Tigeot return 0; 31884dbb207bSFrançois Tigeot 31894dbb207bSFrançois Tigeot fail_lookup: 31904dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 31914dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 31924dbb207bSFrançois Tigeot 31939edbd4a0SFrançois Tigeot return -ENOENT; 31945718399fSFrançois Tigeot } 31955718399fSFrançois Tigeot 31965718399fSFrançois Tigeot /** 31975718399fSFrançois Tigeot * drm_mode_getfb - get FB info 31984dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 31994dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 32004dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 32015718399fSFrançois Tigeot * 32025718399fSFrançois Tigeot * Lookup the FB given its ID and return info about it. 32035718399fSFrançois Tigeot * 32045718399fSFrançois Tigeot * Called by the user via ioctl. 32055718399fSFrançois Tigeot * 3206ba55f2f5SFrançois Tigeot * Returns: 32075718399fSFrançois Tigeot * Zero on success, errno on failure. 32085718399fSFrançois Tigeot */ 32095718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev, 32105718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 32115718399fSFrançois Tigeot { 32125718399fSFrançois Tigeot struct drm_mode_fb_cmd *r = data; 32135718399fSFrançois Tigeot struct drm_framebuffer *fb; 32144dbb207bSFrançois Tigeot int ret; 32155718399fSFrançois Tigeot 32165718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3217b5162e19SFrançois Tigeot return -EINVAL; 32185718399fSFrançois Tigeot 32194dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 32204dbb207bSFrançois Tigeot if (!fb) 32219edbd4a0SFrançois Tigeot return -ENOENT; 32225718399fSFrançois Tigeot 32235718399fSFrançois Tigeot r->height = fb->height; 32245718399fSFrançois Tigeot r->width = fb->width; 32255718399fSFrançois Tigeot r->depth = fb->depth; 32265718399fSFrançois Tigeot r->bpp = fb->bits_per_pixel; 32275718399fSFrançois Tigeot r->pitch = fb->pitches[0]; 3228ba55f2f5SFrançois Tigeot if (fb->funcs->create_handle) { 32294dbb207bSFrançois Tigeot ret = fb->funcs->create_handle(fb, file_priv, &r->handle); 3230ba55f2f5SFrançois Tigeot } else { 32314dbb207bSFrançois Tigeot ret = -ENODEV; 3232ba55f2f5SFrançois Tigeot } 32335718399fSFrançois Tigeot 32344dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 32354dbb207bSFrançois Tigeot 32365718399fSFrançois Tigeot return ret; 32375718399fSFrançois Tigeot } 32385718399fSFrançois Tigeot 3239ba55f2f5SFrançois Tigeot /** 3240ba55f2f5SFrançois Tigeot * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB 3241ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 3242ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 3243ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 3244ba55f2f5SFrançois Tigeot * 3245ba55f2f5SFrançois Tigeot * Lookup the FB and flush out the damaged area supplied by userspace as a clip 3246ba55f2f5SFrançois Tigeot * rectangle list. Generic userspace which does frontbuffer rendering must call 3247ba55f2f5SFrançois Tigeot * this ioctl to flush out the changes on manual-update display outputs, e.g. 3248ba55f2f5SFrançois Tigeot * usb display-link, mipi manual update panels or edp panel self refresh modes. 3249ba55f2f5SFrançois Tigeot * 3250ba55f2f5SFrançois Tigeot * Modesetting drivers which always update the frontbuffer do not need to 3251ba55f2f5SFrançois Tigeot * implement the corresponding ->dirty framebuffer callback. 3252ba55f2f5SFrançois Tigeot * 3253ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3254ba55f2f5SFrançois Tigeot * 3255ba55f2f5SFrançois Tigeot * Returns: 3256ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 3257ba55f2f5SFrançois Tigeot */ 32585718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev, 32595718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 32605718399fSFrançois Tigeot { 32615718399fSFrançois Tigeot struct drm_clip_rect __user *clips_ptr; 32625718399fSFrançois Tigeot struct drm_clip_rect *clips = NULL; 32635718399fSFrançois Tigeot struct drm_mode_fb_dirty_cmd *r = data; 32645718399fSFrançois Tigeot struct drm_framebuffer *fb; 32655718399fSFrançois Tigeot unsigned flags; 32665718399fSFrançois Tigeot int num_clips; 3267b5162e19SFrançois Tigeot int ret; 32685718399fSFrançois Tigeot 32695718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3270b5162e19SFrançois Tigeot return -EINVAL; 32715718399fSFrançois Tigeot 32724dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 32734dbb207bSFrançois Tigeot if (!fb) 32749edbd4a0SFrançois Tigeot return -ENOENT; 32755718399fSFrançois Tigeot 32765718399fSFrançois Tigeot num_clips = r->num_clips; 3277b5162e19SFrançois Tigeot clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 32785718399fSFrançois Tigeot 32795718399fSFrançois Tigeot if (!num_clips != !clips_ptr) { 3280b5162e19SFrançois Tigeot ret = -EINVAL; 32815718399fSFrançois Tigeot goto out_err1; 32825718399fSFrançois Tigeot } 32835718399fSFrançois Tigeot 32845718399fSFrançois Tigeot flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 32855718399fSFrançois Tigeot 32865718399fSFrançois Tigeot /* If userspace annotates copy, clips must come in pairs */ 32875718399fSFrançois Tigeot if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 3288b5162e19SFrançois Tigeot ret = -EINVAL; 32895718399fSFrançois Tigeot goto out_err1; 32905718399fSFrançois Tigeot } 32915718399fSFrançois Tigeot 32925718399fSFrançois Tigeot if (num_clips && clips_ptr) { 32935718399fSFrançois Tigeot if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 3294b5162e19SFrançois Tigeot ret = -EINVAL; 32955718399fSFrançois Tigeot goto out_err1; 32965718399fSFrançois Tigeot } 32974dbb207bSFrançois Tigeot clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); 3298b5162e19SFrançois Tigeot if (!clips) { 3299b5162e19SFrançois Tigeot ret = -ENOMEM; 3300b5162e19SFrançois Tigeot goto out_err1; 3301b5162e19SFrançois Tigeot } 33025718399fSFrançois Tigeot 3303b5162e19SFrançois Tigeot ret = copy_from_user(clips, clips_ptr, 3304b5162e19SFrançois Tigeot num_clips * sizeof(*clips)); 3305b5162e19SFrançois Tigeot if (ret) { 3306b5162e19SFrançois Tigeot ret = -EFAULT; 33075718399fSFrançois Tigeot goto out_err2; 33085718399fSFrançois Tigeot } 3309b5162e19SFrançois Tigeot } 33105718399fSFrançois Tigeot 33115718399fSFrançois Tigeot if (fb->funcs->dirty) { 3312b5162e19SFrançois Tigeot ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 33135718399fSFrançois Tigeot clips, num_clips); 33145718399fSFrançois Tigeot } else { 3315b5162e19SFrançois Tigeot ret = -ENOSYS; 33165718399fSFrançois Tigeot } 33175718399fSFrançois Tigeot 33185718399fSFrançois Tigeot out_err2: 33194dbb207bSFrançois Tigeot kfree(clips); 33205718399fSFrançois Tigeot out_err1: 33214dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 33224dbb207bSFrançois Tigeot 33235718399fSFrançois Tigeot return ret; 33245718399fSFrançois Tigeot } 33255718399fSFrançois Tigeot 33265718399fSFrançois Tigeot 33275718399fSFrançois Tigeot /** 33285718399fSFrançois Tigeot * drm_fb_release - remove and free the FBs on this file 33294dbb207bSFrançois Tigeot * @priv: drm file for the ioctl 33305718399fSFrançois Tigeot * 33315718399fSFrançois Tigeot * Destroy all the FBs associated with @filp. 33325718399fSFrançois Tigeot * 33335718399fSFrançois Tigeot * Called by the user via ioctl. 33345718399fSFrançois Tigeot * 3335ba55f2f5SFrançois Tigeot * Returns: 33365718399fSFrançois Tigeot * Zero on success, errno on failure. 33375718399fSFrançois Tigeot */ 33385718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv) 33395718399fSFrançois Tigeot { 33404dbb207bSFrançois Tigeot struct drm_device *dev = priv->dev; 33415718399fSFrançois Tigeot struct drm_framebuffer *fb, *tfb; 33425718399fSFrançois Tigeot 33434dbb207bSFrançois Tigeot mutex_lock(&priv->fbs_lock); 33445718399fSFrançois Tigeot list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 33454dbb207bSFrançois Tigeot 33464dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 33474dbb207bSFrançois Tigeot /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ 33484dbb207bSFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 33494dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 33504dbb207bSFrançois Tigeot 33514dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 33524dbb207bSFrançois Tigeot 33534dbb207bSFrançois Tigeot /* This will also drop the fpriv->fbs reference. */ 3354b5162e19SFrançois Tigeot drm_framebuffer_remove(fb); 33555718399fSFrançois Tigeot } 33564dbb207bSFrançois Tigeot mutex_unlock(&priv->fbs_lock); 33575718399fSFrançois Tigeot } 33585718399fSFrançois Tigeot 3359ba55f2f5SFrançois Tigeot /** 3360ba55f2f5SFrançois Tigeot * drm_property_create - create a new property type 3361ba55f2f5SFrançois Tigeot * @dev: drm device 3362ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3363ba55f2f5SFrançois Tigeot * @name: name of the property 3364ba55f2f5SFrançois Tigeot * @num_values: number of pre-defined values 3365ba55f2f5SFrançois Tigeot * 3366ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3367ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3368ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3369ba55f2f5SFrançois Tigeot * 3370ba55f2f5SFrançois Tigeot * Returns: 3371ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3372ba55f2f5SFrançois Tigeot */ 33735718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags, 33745718399fSFrançois Tigeot const char *name, int num_values) 33755718399fSFrançois Tigeot { 33765718399fSFrançois Tigeot struct drm_property *property = NULL; 33775718399fSFrançois Tigeot int ret; 33785718399fSFrançois Tigeot 33794dbb207bSFrançois Tigeot property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); 3380b5162e19SFrançois Tigeot if (!property) 3381b5162e19SFrançois Tigeot return NULL; 33825718399fSFrançois Tigeot 3383ba55f2f5SFrançois Tigeot property->dev = dev; 3384ba55f2f5SFrançois Tigeot 33855718399fSFrançois Tigeot if (num_values) { 33864dbb207bSFrançois Tigeot property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); 3387b5162e19SFrançois Tigeot if (!property->values) 3388b5162e19SFrançois Tigeot goto fail; 33895718399fSFrançois Tigeot } 33905718399fSFrançois Tigeot 33915718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); 33925718399fSFrançois Tigeot if (ret) 33935718399fSFrançois Tigeot goto fail; 3394b5162e19SFrançois Tigeot 33955718399fSFrançois Tigeot property->flags = flags; 33965718399fSFrançois Tigeot property->num_values = num_values; 33975718399fSFrançois Tigeot INIT_LIST_HEAD(&property->enum_blob_list); 33985718399fSFrançois Tigeot 33995718399fSFrançois Tigeot if (name) { 34005718399fSFrançois Tigeot strncpy(property->name, name, DRM_PROP_NAME_LEN); 34015718399fSFrançois Tigeot property->name[DRM_PROP_NAME_LEN-1] = '\0'; 34025718399fSFrançois Tigeot } 34035718399fSFrançois Tigeot 34045718399fSFrançois Tigeot list_add_tail(&property->head, &dev->mode_config.property_list); 3405ba55f2f5SFrançois Tigeot 3406ba55f2f5SFrançois Tigeot WARN_ON(!drm_property_type_valid(property)); 3407ba55f2f5SFrançois Tigeot 34085718399fSFrançois Tigeot return property; 34095718399fSFrançois Tigeot fail: 34104dbb207bSFrançois Tigeot kfree(property->values); 34114dbb207bSFrançois Tigeot kfree(property); 3412b5162e19SFrançois Tigeot return NULL; 34135718399fSFrançois Tigeot } 3414b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create); 34155718399fSFrançois Tigeot 3416ba55f2f5SFrançois Tigeot /** 3417*24edb884SFrançois Tigeot * drm_property_create_enum - create a new enumeration property type 3418ba55f2f5SFrançois Tigeot * @dev: drm device 3419ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3420ba55f2f5SFrançois Tigeot * @name: name of the property 3421ba55f2f5SFrançois Tigeot * @props: enumeration lists with property values 3422ba55f2f5SFrançois Tigeot * @num_values: number of pre-defined values 3423ba55f2f5SFrançois Tigeot * 3424ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3425ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3426ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3427ba55f2f5SFrançois Tigeot * 3428ba55f2f5SFrançois Tigeot * Userspace is only allowed to set one of the predefined values for enumeration 3429ba55f2f5SFrançois Tigeot * properties. 3430ba55f2f5SFrançois Tigeot * 3431ba55f2f5SFrançois Tigeot * Returns: 3432ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3433ba55f2f5SFrançois Tigeot */ 34345718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, 34355718399fSFrançois Tigeot const char *name, 34365718399fSFrançois Tigeot const struct drm_prop_enum_list *props, 34375718399fSFrançois Tigeot int num_values) 34385718399fSFrançois Tigeot { 34395718399fSFrançois Tigeot struct drm_property *property; 34405718399fSFrançois Tigeot int i, ret; 34415718399fSFrançois Tigeot 34425718399fSFrançois Tigeot flags |= DRM_MODE_PROP_ENUM; 34435718399fSFrançois Tigeot 34445718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 34455718399fSFrançois Tigeot if (!property) 34465718399fSFrançois Tigeot return NULL; 34475718399fSFrançois Tigeot 34485718399fSFrançois Tigeot for (i = 0; i < num_values; i++) { 34495718399fSFrançois Tigeot ret = drm_property_add_enum(property, i, 34505718399fSFrançois Tigeot props[i].type, 34515718399fSFrançois Tigeot props[i].name); 34525718399fSFrançois Tigeot if (ret) { 34535718399fSFrançois Tigeot drm_property_destroy(dev, property); 34545718399fSFrançois Tigeot return NULL; 34555718399fSFrançois Tigeot } 34565718399fSFrançois Tigeot } 34575718399fSFrançois Tigeot 34585718399fSFrançois Tigeot return property; 34595718399fSFrançois Tigeot } 3460b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum); 3461b5162e19SFrançois Tigeot 3462ba55f2f5SFrançois Tigeot /** 3463*24edb884SFrançois Tigeot * drm_property_create_bitmask - create a new bitmask property type 3464ba55f2f5SFrançois Tigeot * @dev: drm device 3465ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3466ba55f2f5SFrançois Tigeot * @name: name of the property 3467ba55f2f5SFrançois Tigeot * @props: enumeration lists with property bitflags 3468ba55f2f5SFrançois Tigeot * @num_values: number of pre-defined values 3469ba55f2f5SFrançois Tigeot * 3470ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3471ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3472ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3473ba55f2f5SFrançois Tigeot * 3474ba55f2f5SFrançois Tigeot * Compared to plain enumeration properties userspace is allowed to set any 3475ba55f2f5SFrançois Tigeot * or'ed together combination of the predefined property bitflag values 3476ba55f2f5SFrançois Tigeot * 3477ba55f2f5SFrançois Tigeot * Returns: 3478ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3479ba55f2f5SFrançois Tigeot */ 3480b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev, 3481b5162e19SFrançois Tigeot int flags, const char *name, 3482b5162e19SFrançois Tigeot const struct drm_prop_enum_list *props, 3483b5162e19SFrançois Tigeot int num_values) 3484b5162e19SFrançois Tigeot { 3485b5162e19SFrançois Tigeot struct drm_property *property; 3486b5162e19SFrançois Tigeot int i, ret; 3487b5162e19SFrançois Tigeot 3488b5162e19SFrançois Tigeot flags |= DRM_MODE_PROP_BITMASK; 3489b5162e19SFrançois Tigeot 3490b5162e19SFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 3491b5162e19SFrançois Tigeot if (!property) 3492b5162e19SFrançois Tigeot return NULL; 3493b5162e19SFrançois Tigeot 3494b5162e19SFrançois Tigeot for (i = 0; i < num_values; i++) { 3495b5162e19SFrançois Tigeot ret = drm_property_add_enum(property, i, 3496b5162e19SFrançois Tigeot props[i].type, 3497b5162e19SFrançois Tigeot props[i].name); 3498b5162e19SFrançois Tigeot if (ret) { 3499b5162e19SFrançois Tigeot drm_property_destroy(dev, property); 3500b5162e19SFrançois Tigeot return NULL; 3501b5162e19SFrançois Tigeot } 3502b5162e19SFrançois Tigeot } 3503b5162e19SFrançois Tigeot 3504b5162e19SFrançois Tigeot return property; 3505b5162e19SFrançois Tigeot } 3506b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask); 35075718399fSFrançois Tigeot 3508ba55f2f5SFrançois Tigeot static struct drm_property *property_create_range(struct drm_device *dev, 3509ba55f2f5SFrançois Tigeot int flags, const char *name, 35105718399fSFrançois Tigeot uint64_t min, uint64_t max) 35115718399fSFrançois Tigeot { 35125718399fSFrançois Tigeot struct drm_property *property; 35135718399fSFrançois Tigeot 35145718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, 2); 35155718399fSFrançois Tigeot if (!property) 35165718399fSFrançois Tigeot return NULL; 35175718399fSFrançois Tigeot 35185718399fSFrançois Tigeot property->values[0] = min; 35195718399fSFrançois Tigeot property->values[1] = max; 35205718399fSFrançois Tigeot 35215718399fSFrançois Tigeot return property; 35225718399fSFrançois Tigeot } 3523ba55f2f5SFrançois Tigeot 3524ba55f2f5SFrançois Tigeot /** 3525*24edb884SFrançois Tigeot * drm_property_create_range - create a new ranged property type 3526ba55f2f5SFrançois Tigeot * @dev: drm device 3527ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3528ba55f2f5SFrançois Tigeot * @name: name of the property 3529ba55f2f5SFrançois Tigeot * @min: minimum value of the property 3530ba55f2f5SFrançois Tigeot * @max: maximum value of the property 3531ba55f2f5SFrançois Tigeot * 3532ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3533ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3534ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3535ba55f2f5SFrançois Tigeot * 3536ba55f2f5SFrançois Tigeot * Userspace is allowed to set any interger value in the (min, max) range 3537ba55f2f5SFrançois Tigeot * inclusive. 3538ba55f2f5SFrançois Tigeot * 3539ba55f2f5SFrançois Tigeot * Returns: 3540ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3541ba55f2f5SFrançois Tigeot */ 3542ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, 3543ba55f2f5SFrançois Tigeot const char *name, 3544ba55f2f5SFrançois Tigeot uint64_t min, uint64_t max) 3545ba55f2f5SFrançois Tigeot { 3546ba55f2f5SFrançois Tigeot return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, 3547ba55f2f5SFrançois Tigeot name, min, max); 3548ba55f2f5SFrançois Tigeot } 3549b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range); 35505718399fSFrançois Tigeot 3551ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_signed_range(struct drm_device *dev, 3552ba55f2f5SFrançois Tigeot int flags, const char *name, 3553ba55f2f5SFrançois Tigeot int64_t min, int64_t max) 3554ba55f2f5SFrançois Tigeot { 3555ba55f2f5SFrançois Tigeot return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, 3556ba55f2f5SFrançois Tigeot name, I642U64(min), I642U64(max)); 3557ba55f2f5SFrançois Tigeot } 3558ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_signed_range); 3559ba55f2f5SFrançois Tigeot 3560ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_object(struct drm_device *dev, 3561ba55f2f5SFrançois Tigeot int flags, const char *name, uint32_t type) 3562ba55f2f5SFrançois Tigeot { 3563ba55f2f5SFrançois Tigeot struct drm_property *property; 3564ba55f2f5SFrançois Tigeot 3565ba55f2f5SFrançois Tigeot flags |= DRM_MODE_PROP_OBJECT; 3566ba55f2f5SFrançois Tigeot 3567ba55f2f5SFrançois Tigeot property = drm_property_create(dev, flags, name, 1); 3568ba55f2f5SFrançois Tigeot if (!property) 3569ba55f2f5SFrançois Tigeot return NULL; 3570ba55f2f5SFrançois Tigeot 3571ba55f2f5SFrançois Tigeot property->values[0] = type; 3572ba55f2f5SFrançois Tigeot 3573ba55f2f5SFrançois Tigeot return property; 3574ba55f2f5SFrançois Tigeot } 3575ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_object); 3576ba55f2f5SFrançois Tigeot 3577ba55f2f5SFrançois Tigeot /** 3578ba55f2f5SFrançois Tigeot * drm_property_add_enum - add a possible value to an enumeration property 3579ba55f2f5SFrançois Tigeot * @property: enumeration property to change 3580ba55f2f5SFrançois Tigeot * @index: index of the new enumeration 3581ba55f2f5SFrançois Tigeot * @value: value of the new enumeration 3582ba55f2f5SFrançois Tigeot * @name: symbolic name of the new enumeration 3583ba55f2f5SFrançois Tigeot * 3584ba55f2f5SFrançois Tigeot * This functions adds enumerations to a property. 3585ba55f2f5SFrançois Tigeot * 3586ba55f2f5SFrançois Tigeot * It's use is deprecated, drivers should use one of the more specific helpers 3587ba55f2f5SFrançois Tigeot * to directly create the property with all enumerations already attached. 3588ba55f2f5SFrançois Tigeot * 3589ba55f2f5SFrançois Tigeot * Returns: 3590ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 3591ba55f2f5SFrançois Tigeot */ 35925718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index, 35935718399fSFrançois Tigeot uint64_t value, const char *name) 35945718399fSFrançois Tigeot { 35955718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 35965718399fSFrançois Tigeot 3597ba55f2f5SFrançois Tigeot if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 3598ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) 3599b5162e19SFrançois Tigeot return -EINVAL; 3600b5162e19SFrançois Tigeot 3601b5162e19SFrançois Tigeot /* 3602b5162e19SFrançois Tigeot * Bitmask enum properties have the additional constraint of values 3603b5162e19SFrançois Tigeot * from 0 to 63 3604b5162e19SFrançois Tigeot */ 3605ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && 3606ba55f2f5SFrançois Tigeot (value > 63)) 36075718399fSFrançois Tigeot return -EINVAL; 36085718399fSFrançois Tigeot 36095718399fSFrançois Tigeot if (!list_empty(&property->enum_blob_list)) { 36105718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 36115718399fSFrançois Tigeot if (prop_enum->value == value) { 36125718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 36135718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 36145718399fSFrançois Tigeot return 0; 36155718399fSFrançois Tigeot } 36165718399fSFrançois Tigeot } 36175718399fSFrançois Tigeot } 36185718399fSFrançois Tigeot 36194dbb207bSFrançois Tigeot prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); 3620b5162e19SFrançois Tigeot if (!prop_enum) 3621b5162e19SFrançois Tigeot return -ENOMEM; 36225718399fSFrançois Tigeot 36235718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 36245718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 36255718399fSFrançois Tigeot prop_enum->value = value; 36265718399fSFrançois Tigeot 36275718399fSFrançois Tigeot property->values[index] = value; 36285718399fSFrançois Tigeot list_add_tail(&prop_enum->head, &property->enum_blob_list); 36295718399fSFrançois Tigeot return 0; 36305718399fSFrançois Tigeot } 3631b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum); 36325718399fSFrançois Tigeot 3633ba55f2f5SFrançois Tigeot /** 3634ba55f2f5SFrançois Tigeot * drm_property_destroy - destroy a drm property 3635ba55f2f5SFrançois Tigeot * @dev: drm device 3636ba55f2f5SFrançois Tigeot * @property: property to destry 3637ba55f2f5SFrançois Tigeot * 3638ba55f2f5SFrançois Tigeot * This function frees a property including any attached resources like 3639ba55f2f5SFrançois Tigeot * enumeration values. 3640ba55f2f5SFrançois Tigeot */ 36415718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property) 36425718399fSFrançois Tigeot { 36435718399fSFrançois Tigeot struct drm_property_enum *prop_enum, *pt; 36445718399fSFrançois Tigeot 36455718399fSFrançois Tigeot list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { 36465718399fSFrançois Tigeot list_del(&prop_enum->head); 36474dbb207bSFrançois Tigeot kfree(prop_enum); 36485718399fSFrançois Tigeot } 36495718399fSFrançois Tigeot 36505718399fSFrançois Tigeot if (property->num_values) 36514dbb207bSFrançois Tigeot kfree(property->values); 36525718399fSFrançois Tigeot drm_mode_object_put(dev, &property->base); 36535718399fSFrançois Tigeot list_del(&property->head); 36544dbb207bSFrançois Tigeot kfree(property); 36555718399fSFrançois Tigeot } 3656b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy); 36575718399fSFrançois Tigeot 3658ba55f2f5SFrançois Tigeot /** 3659ba55f2f5SFrançois Tigeot * drm_object_attach_property - attach a property to a modeset object 3660ba55f2f5SFrançois Tigeot * @obj: drm modeset object 3661ba55f2f5SFrançois Tigeot * @property: property to attach 3662ba55f2f5SFrançois Tigeot * @init_val: initial value of the property 3663ba55f2f5SFrançois Tigeot * 3664ba55f2f5SFrançois Tigeot * This attaches the given property to the modeset object with the given initial 3665ba55f2f5SFrançois Tigeot * value. Currently this function cannot fail since the properties are stored in 3666ba55f2f5SFrançois Tigeot * a statically sized array. 3667ba55f2f5SFrançois Tigeot */ 3668b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj, 3669b5162e19SFrançois Tigeot struct drm_property *property, 3670b5162e19SFrançois Tigeot uint64_t init_val) 3671b5162e19SFrançois Tigeot { 3672b5162e19SFrançois Tigeot int count = obj->properties->count; 3673b5162e19SFrançois Tigeot 3674b5162e19SFrançois Tigeot if (count == DRM_OBJECT_MAX_PROPERTY) { 3675b5162e19SFrançois Tigeot WARN(1, "Failed to attach object property (type: 0x%x). Please " 3676b5162e19SFrançois Tigeot "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " 3677b5162e19SFrançois Tigeot "you see this message on the same object type.\n", 3678b5162e19SFrançois Tigeot obj->type); 3679b5162e19SFrançois Tigeot return; 3680b5162e19SFrançois Tigeot } 3681b5162e19SFrançois Tigeot 3682b5162e19SFrançois Tigeot obj->properties->ids[count] = property->base.id; 3683b5162e19SFrançois Tigeot obj->properties->values[count] = init_val; 3684b5162e19SFrançois Tigeot obj->properties->count++; 3685b5162e19SFrançois Tigeot } 3686b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property); 3687b5162e19SFrançois Tigeot 3688ba55f2f5SFrançois Tigeot /** 3689ba55f2f5SFrançois Tigeot * drm_object_property_set_value - set the value of a property 3690ba55f2f5SFrançois Tigeot * @obj: drm mode object to set property value for 3691ba55f2f5SFrançois Tigeot * @property: property to set 3692ba55f2f5SFrançois Tigeot * @val: value the property should be set to 3693ba55f2f5SFrançois Tigeot * 3694ba55f2f5SFrançois Tigeot * This functions sets a given property on a given object. This function only 3695ba55f2f5SFrançois Tigeot * changes the software state of the property, it does not call into the 3696ba55f2f5SFrançois Tigeot * driver's ->set_property callback. 3697ba55f2f5SFrançois Tigeot * 3698ba55f2f5SFrançois Tigeot * Returns: 3699ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 3700ba55f2f5SFrançois Tigeot */ 3701b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj, 3702b5162e19SFrançois Tigeot struct drm_property *property, uint64_t val) 37035718399fSFrançois Tigeot { 37045718399fSFrançois Tigeot int i; 37055718399fSFrançois Tigeot 3706b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 3707b5162e19SFrançois Tigeot if (obj->properties->ids[i] == property->base.id) { 3708b5162e19SFrançois Tigeot obj->properties->values[i] = val; 37095718399fSFrançois Tigeot return 0; 37105718399fSFrançois Tigeot } 37115718399fSFrançois Tigeot } 37125718399fSFrançois Tigeot 37135718399fSFrançois Tigeot return -EINVAL; 37145718399fSFrançois Tigeot } 3715b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value); 37165718399fSFrançois Tigeot 3717ba55f2f5SFrançois Tigeot /** 3718ba55f2f5SFrançois Tigeot * drm_object_property_get_value - retrieve the value of a property 3719ba55f2f5SFrançois Tigeot * @obj: drm mode object to get property value from 3720ba55f2f5SFrançois Tigeot * @property: property to retrieve 3721ba55f2f5SFrançois Tigeot * @val: storage for the property value 3722ba55f2f5SFrançois Tigeot * 3723ba55f2f5SFrançois Tigeot * This function retrieves the softare state of the given property for the given 3724ba55f2f5SFrançois Tigeot * property. Since there is no driver callback to retrieve the current property 3725ba55f2f5SFrançois Tigeot * value this might be out of sync with the hardware, depending upon the driver 3726ba55f2f5SFrançois Tigeot * and property. 3727ba55f2f5SFrançois Tigeot * 3728ba55f2f5SFrançois Tigeot * Returns: 3729ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 3730ba55f2f5SFrançois Tigeot */ 3731b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj, 37325718399fSFrançois Tigeot struct drm_property *property, uint64_t *val) 37335718399fSFrançois Tigeot { 37345718399fSFrançois Tigeot int i; 37355718399fSFrançois Tigeot 3736b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 3737b5162e19SFrançois Tigeot if (obj->properties->ids[i] == property->base.id) { 3738b5162e19SFrançois Tigeot *val = obj->properties->values[i]; 3739b5162e19SFrançois Tigeot return 0; 37405718399fSFrançois Tigeot } 37415718399fSFrançois Tigeot } 37425718399fSFrançois Tigeot 37435718399fSFrançois Tigeot return -EINVAL; 37445718399fSFrançois Tigeot } 3745b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value); 37465718399fSFrançois Tigeot 3747ba55f2f5SFrançois Tigeot /** 3748ba55f2f5SFrançois Tigeot * drm_mode_getproperty_ioctl - get the current value of a connector's property 3749ba55f2f5SFrançois Tigeot * @dev: DRM device 3750ba55f2f5SFrançois Tigeot * @data: ioctl data 3751ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 3752ba55f2f5SFrançois Tigeot * 3753ba55f2f5SFrançois Tigeot * This function retrieves the current value for an connectors's property. 3754ba55f2f5SFrançois Tigeot * 3755ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3756ba55f2f5SFrançois Tigeot * 3757ba55f2f5SFrançois Tigeot * Returns: 3758ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 3759ba55f2f5SFrançois Tigeot */ 37605718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev, 37615718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 37625718399fSFrançois Tigeot { 37635718399fSFrançois Tigeot struct drm_mode_get_property *out_resp = data; 37645718399fSFrançois Tigeot struct drm_property *property; 37655718399fSFrançois Tigeot int enum_count = 0; 37665718399fSFrançois Tigeot int blob_count = 0; 37675718399fSFrançois Tigeot int value_count = 0; 37685718399fSFrançois Tigeot int ret = 0, i; 37695718399fSFrançois Tigeot int copied; 37705718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 37715718399fSFrançois Tigeot struct drm_mode_property_enum __user *enum_ptr; 37725718399fSFrançois Tigeot struct drm_property_blob *prop_blob; 3773b5162e19SFrançois Tigeot uint32_t __user *blob_id_ptr; 3774b5162e19SFrançois Tigeot uint64_t __user *values_ptr; 3775b5162e19SFrançois Tigeot uint32_t __user *blob_length_ptr; 37765718399fSFrançois Tigeot 37775718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 37785718399fSFrançois Tigeot return -EINVAL; 37795718399fSFrançois Tigeot 37804dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 3781ba55f2f5SFrançois Tigeot property = drm_property_find(dev, out_resp->prop_id); 3782ba55f2f5SFrançois Tigeot if (!property) { 37839edbd4a0SFrançois Tigeot ret = -ENOENT; 37845718399fSFrançois Tigeot goto done; 37855718399fSFrançois Tigeot } 37865718399fSFrançois Tigeot 3787ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 3788ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 37895718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) 37905718399fSFrançois Tigeot enum_count++; 3791ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { 37925718399fSFrançois Tigeot list_for_each_entry(prop_blob, &property->enum_blob_list, head) 37935718399fSFrançois Tigeot blob_count++; 37945718399fSFrançois Tigeot } 37955718399fSFrançois Tigeot 37965718399fSFrançois Tigeot value_count = property->num_values; 37975718399fSFrançois Tigeot 37985718399fSFrançois Tigeot strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); 37995718399fSFrançois Tigeot out_resp->name[DRM_PROP_NAME_LEN-1] = 0; 38005718399fSFrançois Tigeot out_resp->flags = property->flags; 38015718399fSFrançois Tigeot 38025718399fSFrançois Tigeot if ((out_resp->count_values >= value_count) && value_count) { 3803b5162e19SFrançois Tigeot values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; 38045718399fSFrançois Tigeot for (i = 0; i < value_count; i++) { 3805b5162e19SFrançois Tigeot if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { 38065718399fSFrançois Tigeot ret = -EFAULT; 38075718399fSFrançois Tigeot goto done; 38085718399fSFrançois Tigeot } 38095718399fSFrançois Tigeot } 38105718399fSFrançois Tigeot } 38115718399fSFrançois Tigeot out_resp->count_values = value_count; 38125718399fSFrançois Tigeot 3813ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 3814ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 38155718399fSFrançois Tigeot if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { 38165718399fSFrançois Tigeot copied = 0; 3817b5162e19SFrançois Tigeot enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; 38185718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 38195718399fSFrançois Tigeot 3820b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { 38215718399fSFrançois Tigeot ret = -EFAULT; 38225718399fSFrançois Tigeot goto done; 38235718399fSFrançois Tigeot } 38245718399fSFrançois Tigeot 3825b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].name, 3826b5162e19SFrançois Tigeot &prop_enum->name, DRM_PROP_NAME_LEN)) { 38275718399fSFrançois Tigeot ret = -EFAULT; 38285718399fSFrançois Tigeot goto done; 38295718399fSFrançois Tigeot } 38305718399fSFrançois Tigeot copied++; 38315718399fSFrançois Tigeot } 38325718399fSFrançois Tigeot } 38335718399fSFrançois Tigeot out_resp->count_enum_blobs = enum_count; 38345718399fSFrançois Tigeot } 38355718399fSFrançois Tigeot 3836ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { 38375718399fSFrançois Tigeot if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { 38385718399fSFrançois Tigeot copied = 0; 3839b5162e19SFrançois Tigeot blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; 3840b5162e19SFrançois Tigeot blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; 38415718399fSFrançois Tigeot 38425718399fSFrançois Tigeot list_for_each_entry(prop_blob, &property->enum_blob_list, head) { 3843b5162e19SFrançois Tigeot if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { 38445718399fSFrançois Tigeot ret = -EFAULT; 38455718399fSFrançois Tigeot goto done; 38465718399fSFrançois Tigeot } 38475718399fSFrançois Tigeot 3848b5162e19SFrançois Tigeot if (put_user(prop_blob->length, blob_length_ptr + copied)) { 38495718399fSFrançois Tigeot ret = -EFAULT; 38505718399fSFrançois Tigeot goto done; 38515718399fSFrançois Tigeot } 38525718399fSFrançois Tigeot 38535718399fSFrançois Tigeot copied++; 38545718399fSFrançois Tigeot } 38555718399fSFrançois Tigeot } 38565718399fSFrançois Tigeot out_resp->count_enum_blobs = blob_count; 38575718399fSFrançois Tigeot } 38585718399fSFrançois Tigeot done: 38594dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 38605718399fSFrançois Tigeot return ret; 38615718399fSFrançois Tigeot } 38625718399fSFrançois Tigeot 38635718399fSFrançois Tigeot static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, 38645718399fSFrançois Tigeot void *data) 38655718399fSFrançois Tigeot { 38665718399fSFrançois Tigeot struct drm_property_blob *blob; 38675718399fSFrançois Tigeot int ret; 38685718399fSFrançois Tigeot 38695718399fSFrançois Tigeot if (!length || !data) 38705718399fSFrançois Tigeot return NULL; 38715718399fSFrançois Tigeot 38724dbb207bSFrançois Tigeot blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); 3873b5162e19SFrançois Tigeot if (!blob) 3874b5162e19SFrançois Tigeot return NULL; 38755718399fSFrançois Tigeot 38765718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); 38775718399fSFrançois Tigeot if (ret) { 38784dbb207bSFrançois Tigeot kfree(blob); 3879b5162e19SFrançois Tigeot return NULL; 38805718399fSFrançois Tigeot } 38815718399fSFrançois Tigeot 38825718399fSFrançois Tigeot blob->length = length; 38835718399fSFrançois Tigeot 38845718399fSFrançois Tigeot memcpy(blob->data, data, length); 38855718399fSFrançois Tigeot 38865718399fSFrançois Tigeot list_add_tail(&blob->head, &dev->mode_config.property_blob_list); 38875718399fSFrançois Tigeot return blob; 38885718399fSFrançois Tigeot } 38895718399fSFrançois Tigeot 38905718399fSFrançois Tigeot static void drm_property_destroy_blob(struct drm_device *dev, 38915718399fSFrançois Tigeot struct drm_property_blob *blob) 38925718399fSFrançois Tigeot { 38935718399fSFrançois Tigeot drm_mode_object_put(dev, &blob->base); 38945718399fSFrançois Tigeot list_del(&blob->head); 38954dbb207bSFrançois Tigeot kfree(blob); 38965718399fSFrançois Tigeot } 38975718399fSFrançois Tigeot 3898ba55f2f5SFrançois Tigeot /** 3899ba55f2f5SFrançois Tigeot * drm_mode_getblob_ioctl - get the contents of a blob property value 3900ba55f2f5SFrançois Tigeot * @dev: DRM device 3901ba55f2f5SFrançois Tigeot * @data: ioctl data 3902ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 3903ba55f2f5SFrançois Tigeot * 3904ba55f2f5SFrançois Tigeot * This function retrieves the contents of a blob property. The value stored in 3905ba55f2f5SFrançois Tigeot * an object's blob property is just a normal modeset object id. 3906ba55f2f5SFrançois Tigeot * 3907ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3908ba55f2f5SFrançois Tigeot * 3909ba55f2f5SFrançois Tigeot * Returns: 3910ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 3911ba55f2f5SFrançois Tigeot */ 39125718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev, 39135718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 39145718399fSFrançois Tigeot { 39155718399fSFrançois Tigeot struct drm_mode_get_blob *out_resp = data; 39165718399fSFrançois Tigeot struct drm_property_blob *blob; 39175718399fSFrançois Tigeot int ret = 0; 3918b5162e19SFrançois Tigeot void __user *blob_ptr; 39195718399fSFrançois Tigeot 39205718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 39215718399fSFrançois Tigeot return -EINVAL; 39225718399fSFrançois Tigeot 39234dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 3924ba55f2f5SFrançois Tigeot blob = drm_property_blob_find(dev, out_resp->blob_id); 3925ba55f2f5SFrançois Tigeot if (!blob) { 39269edbd4a0SFrançois Tigeot ret = -ENOENT; 39275718399fSFrançois Tigeot goto done; 39285718399fSFrançois Tigeot } 39295718399fSFrançois Tigeot 39305718399fSFrançois Tigeot if (out_resp->length == blob->length) { 3931b5162e19SFrançois Tigeot blob_ptr = (void __user *)(unsigned long)out_resp->data; 3932b5162e19SFrançois Tigeot if (copy_to_user(blob_ptr, blob->data, blob->length)){ 39335718399fSFrançois Tigeot ret = -EFAULT; 39345718399fSFrançois Tigeot goto done; 39355718399fSFrançois Tigeot } 39365718399fSFrançois Tigeot } 39375718399fSFrançois Tigeot out_resp->length = blob->length; 39385718399fSFrançois Tigeot 39395718399fSFrançois Tigeot done: 39404dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 39415718399fSFrançois Tigeot return ret; 39425718399fSFrançois Tigeot } 39435718399fSFrançois Tigeot 3944*24edb884SFrançois Tigeot int drm_mode_connector_set_path_property(struct drm_connector *connector, 3945*24edb884SFrançois Tigeot char *path) 3946*24edb884SFrançois Tigeot { 3947*24edb884SFrançois Tigeot struct drm_device *dev = connector->dev; 3948*24edb884SFrançois Tigeot int ret, size; 3949*24edb884SFrançois Tigeot size = strlen(path) + 1; 3950*24edb884SFrançois Tigeot 3951*24edb884SFrançois Tigeot connector->path_blob_ptr = drm_property_create_blob(connector->dev, 3952*24edb884SFrançois Tigeot size, path); 3953*24edb884SFrançois Tigeot if (!connector->path_blob_ptr) 3954*24edb884SFrançois Tigeot return -EINVAL; 3955*24edb884SFrançois Tigeot 3956*24edb884SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, 3957*24edb884SFrançois Tigeot dev->mode_config.path_property, 3958*24edb884SFrançois Tigeot connector->path_blob_ptr->base.id); 3959*24edb884SFrançois Tigeot return ret; 3960*24edb884SFrançois Tigeot } 3961*24edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_path_property); 3962*24edb884SFrançois Tigeot 3963ba55f2f5SFrançois Tigeot /** 3964ba55f2f5SFrançois Tigeot * drm_mode_connector_update_edid_property - update the edid property of a connector 3965ba55f2f5SFrançois Tigeot * @connector: drm connector 3966ba55f2f5SFrançois Tigeot * @edid: new value of the edid property 3967ba55f2f5SFrançois Tigeot * 3968ba55f2f5SFrançois Tigeot * This function creates a new blob modeset object and assigns its id to the 3969ba55f2f5SFrançois Tigeot * connector's edid property. 3970ba55f2f5SFrançois Tigeot * 3971ba55f2f5SFrançois Tigeot * Returns: 3972ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 3973ba55f2f5SFrançois Tigeot */ 39745718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector, 39755718399fSFrançois Tigeot struct edid *edid) 39765718399fSFrançois Tigeot { 39775718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 3978b5162e19SFrançois Tigeot int ret, size; 39795718399fSFrançois Tigeot 3980*24edb884SFrançois Tigeot /* ignore requests to set edid when overridden */ 3981*24edb884SFrançois Tigeot if (connector->override_edid) 3982*24edb884SFrançois Tigeot return 0; 3983*24edb884SFrançois Tigeot 39845718399fSFrançois Tigeot if (connector->edid_blob_ptr) 39855718399fSFrançois Tigeot drm_property_destroy_blob(dev, connector->edid_blob_ptr); 39865718399fSFrançois Tigeot 39875718399fSFrançois Tigeot /* Delete edid, when there is none. */ 39885718399fSFrançois Tigeot if (!edid) { 39895718399fSFrançois Tigeot connector->edid_blob_ptr = NULL; 3990b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); 39915718399fSFrançois Tigeot return ret; 39925718399fSFrançois Tigeot } 39935718399fSFrançois Tigeot 39945718399fSFrançois Tigeot size = EDID_LENGTH * (1 + edid->extensions); 39955718399fSFrançois Tigeot connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 39965718399fSFrançois Tigeot size, edid); 3997b5162e19SFrançois Tigeot if (!connector->edid_blob_ptr) 3998b5162e19SFrançois Tigeot return -EINVAL; 39995718399fSFrançois Tigeot 4000b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, 40015718399fSFrançois Tigeot dev->mode_config.edid_property, 40025718399fSFrançois Tigeot connector->edid_blob_ptr->base.id); 40035718399fSFrançois Tigeot 40045718399fSFrançois Tigeot return ret; 40055718399fSFrançois Tigeot } 4006b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property); 4007b5162e19SFrançois Tigeot 4008b5162e19SFrançois Tigeot static bool drm_property_change_is_valid(struct drm_property *property, 4009b5162e19SFrançois Tigeot uint64_t value) 4010b5162e19SFrançois Tigeot { 4011b5162e19SFrançois Tigeot if (property->flags & DRM_MODE_PROP_IMMUTABLE) 4012b5162e19SFrançois Tigeot return false; 4013ba55f2f5SFrançois Tigeot 4014ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { 4015b5162e19SFrançois Tigeot if (value < property->values[0] || value > property->values[1]) 4016b5162e19SFrançois Tigeot return false; 4017b5162e19SFrançois Tigeot return true; 4018ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { 4019ba55f2f5SFrançois Tigeot int64_t svalue = U642I64(value); 4020ba55f2f5SFrançois Tigeot if (svalue < U642I64(property->values[0]) || 4021ba55f2f5SFrançois Tigeot svalue > U642I64(property->values[1])) 4022ba55f2f5SFrançois Tigeot return false; 4023ba55f2f5SFrançois Tigeot return true; 4024ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 4025b5162e19SFrançois Tigeot int i; 4026b5162e19SFrançois Tigeot uint64_t valid_mask = 0; 4027b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 4028b5162e19SFrançois Tigeot valid_mask |= (1ULL << property->values[i]); 4029b5162e19SFrançois Tigeot return !(value & ~valid_mask); 4030ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { 4031b5162e19SFrançois Tigeot /* Only the driver knows */ 4032b5162e19SFrançois Tigeot return true; 4033ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { 4034ba55f2f5SFrançois Tigeot struct drm_mode_object *obj; 4035ba55f2f5SFrançois Tigeot /* a zero value for an object property translates to null: */ 4036ba55f2f5SFrançois Tigeot if (value == 0) 4037ba55f2f5SFrançois Tigeot return true; 4038ba55f2f5SFrançois Tigeot /* 4039ba55f2f5SFrançois Tigeot * NOTE: use _object_find() directly to bypass restriction on 4040ba55f2f5SFrançois Tigeot * looking up refcnt'd objects (ie. fb's). For a refcnt'd 4041ba55f2f5SFrançois Tigeot * object this could race against object finalization, so it 4042ba55f2f5SFrançois Tigeot * simply tells us that the object *was* valid. Which is good 4043ba55f2f5SFrançois Tigeot * enough. 4044ba55f2f5SFrançois Tigeot */ 4045ba55f2f5SFrançois Tigeot obj = _object_find(property->dev, value, property->values[0]); 4046ba55f2f5SFrançois Tigeot return obj != NULL; 4047b5162e19SFrançois Tigeot } else { 4048b5162e19SFrançois Tigeot int i; 4049b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 4050b5162e19SFrançois Tigeot if (property->values[i] == value) 4051b5162e19SFrançois Tigeot return true; 4052b5162e19SFrançois Tigeot return false; 4053b5162e19SFrançois Tigeot } 4054b5162e19SFrançois Tigeot } 40555718399fSFrançois Tigeot 4056ba55f2f5SFrançois Tigeot /** 4057ba55f2f5SFrançois Tigeot * drm_mode_connector_property_set_ioctl - set the current value of a connector property 4058ba55f2f5SFrançois Tigeot * @dev: DRM device 4059ba55f2f5SFrançois Tigeot * @data: ioctl data 4060ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4061ba55f2f5SFrançois Tigeot * 4062ba55f2f5SFrançois Tigeot * This function sets the current value for a connectors's property. It also 4063ba55f2f5SFrançois Tigeot * calls into a driver's ->set_property callback to update the hardware state 4064ba55f2f5SFrançois Tigeot * 4065ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4066ba55f2f5SFrançois Tigeot * 4067ba55f2f5SFrançois Tigeot * Returns: 4068ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4069ba55f2f5SFrançois Tigeot */ 40705718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev, 40715718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 40725718399fSFrançois Tigeot { 4073b5162e19SFrançois Tigeot struct drm_mode_connector_set_property *conn_set_prop = data; 4074b5162e19SFrançois Tigeot struct drm_mode_obj_set_property obj_set_prop = { 4075b5162e19SFrançois Tigeot .value = conn_set_prop->value, 4076b5162e19SFrançois Tigeot .prop_id = conn_set_prop->prop_id, 4077b5162e19SFrançois Tigeot .obj_id = conn_set_prop->connector_id, 4078b5162e19SFrançois Tigeot .obj_type = DRM_MODE_OBJECT_CONNECTOR 4079b5162e19SFrançois Tigeot }; 4080b5162e19SFrançois Tigeot 4081b5162e19SFrançois Tigeot /* It does all the locking and checking we need */ 4082b5162e19SFrançois Tigeot return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); 4083b5162e19SFrançois Tigeot } 4084b5162e19SFrançois Tigeot 4085b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, 4086b5162e19SFrançois Tigeot struct drm_property *property, 4087b5162e19SFrançois Tigeot uint64_t value) 4088b5162e19SFrançois Tigeot { 4089b5162e19SFrançois Tigeot int ret = -EINVAL; 4090b5162e19SFrançois Tigeot struct drm_connector *connector = obj_to_connector(obj); 4091b5162e19SFrançois Tigeot 4092b5162e19SFrançois Tigeot /* Do DPMS ourselves */ 4093b5162e19SFrançois Tigeot if (property == connector->dev->mode_config.dpms_property) { 4094b5162e19SFrançois Tigeot if (connector->funcs->dpms) 4095b5162e19SFrançois Tigeot (*connector->funcs->dpms)(connector, (int)value); 4096b5162e19SFrançois Tigeot ret = 0; 4097b5162e19SFrançois Tigeot } else if (connector->funcs->set_property) 4098b5162e19SFrançois Tigeot ret = connector->funcs->set_property(connector, property, value); 4099b5162e19SFrançois Tigeot 4100b5162e19SFrançois Tigeot /* store the property value if successful */ 4101b5162e19SFrançois Tigeot if (!ret) 4102b5162e19SFrançois Tigeot drm_object_property_set_value(&connector->base, property, value); 4103b5162e19SFrançois Tigeot return ret; 4104b5162e19SFrançois Tigeot } 4105b5162e19SFrançois Tigeot 4106b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, 4107b5162e19SFrançois Tigeot struct drm_property *property, 4108b5162e19SFrançois Tigeot uint64_t value) 4109b5162e19SFrançois Tigeot { 4110b5162e19SFrançois Tigeot int ret = -EINVAL; 4111b5162e19SFrançois Tigeot struct drm_crtc *crtc = obj_to_crtc(obj); 4112b5162e19SFrançois Tigeot 4113b5162e19SFrançois Tigeot if (crtc->funcs->set_property) 4114b5162e19SFrançois Tigeot ret = crtc->funcs->set_property(crtc, property, value); 4115b5162e19SFrançois Tigeot if (!ret) 4116b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 4117b5162e19SFrançois Tigeot 4118b5162e19SFrançois Tigeot return ret; 4119b5162e19SFrançois Tigeot } 4120b5162e19SFrançois Tigeot 4121b5162e19SFrançois Tigeot static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, 4122b5162e19SFrançois Tigeot struct drm_property *property, 4123b5162e19SFrançois Tigeot uint64_t value) 4124b5162e19SFrançois Tigeot { 4125b5162e19SFrançois Tigeot int ret = -EINVAL; 4126b5162e19SFrançois Tigeot struct drm_plane *plane = obj_to_plane(obj); 4127b5162e19SFrançois Tigeot 4128b5162e19SFrançois Tigeot if (plane->funcs->set_property) 4129b5162e19SFrançois Tigeot ret = plane->funcs->set_property(plane, property, value); 4130b5162e19SFrançois Tigeot if (!ret) 4131b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 4132b5162e19SFrançois Tigeot 4133b5162e19SFrançois Tigeot return ret; 4134b5162e19SFrançois Tigeot } 4135b5162e19SFrançois Tigeot 4136ba55f2f5SFrançois Tigeot /** 4137ba55f2f5SFrançois Tigeot * drm_mode_getproperty_ioctl - get the current value of a object's property 4138ba55f2f5SFrançois Tigeot * @dev: DRM device 4139ba55f2f5SFrançois Tigeot * @data: ioctl data 4140ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4141ba55f2f5SFrançois Tigeot * 4142ba55f2f5SFrançois Tigeot * This function retrieves the current value for an object's property. Compared 4143ba55f2f5SFrançois Tigeot * to the connector specific ioctl this one is extended to also work on crtc and 4144ba55f2f5SFrançois Tigeot * plane objects. 4145ba55f2f5SFrançois Tigeot * 4146ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4147ba55f2f5SFrançois Tigeot * 4148ba55f2f5SFrançois Tigeot * Returns: 4149ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4150ba55f2f5SFrançois Tigeot */ 4151b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, 4152b5162e19SFrançois Tigeot struct drm_file *file_priv) 4153b5162e19SFrançois Tigeot { 4154b5162e19SFrançois Tigeot struct drm_mode_obj_get_properties *arg = data; 41555718399fSFrançois Tigeot struct drm_mode_object *obj; 4156b5162e19SFrançois Tigeot int ret = 0; 4157b5162e19SFrançois Tigeot int i; 4158b5162e19SFrançois Tigeot int copied = 0; 4159b5162e19SFrançois Tigeot int props_count = 0; 4160b5162e19SFrançois Tigeot uint32_t __user *props_ptr; 4161b5162e19SFrançois Tigeot uint64_t __user *prop_values_ptr; 4162b5162e19SFrançois Tigeot 4163b5162e19SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 4164b5162e19SFrançois Tigeot return -EINVAL; 4165b5162e19SFrançois Tigeot 41664dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 4167b5162e19SFrançois Tigeot 4168b5162e19SFrançois Tigeot obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 4169b5162e19SFrançois Tigeot if (!obj) { 41709edbd4a0SFrançois Tigeot ret = -ENOENT; 4171b5162e19SFrançois Tigeot goto out; 4172b5162e19SFrançois Tigeot } 4173b5162e19SFrançois Tigeot if (!obj->properties) { 4174b5162e19SFrançois Tigeot ret = -EINVAL; 4175b5162e19SFrançois Tigeot goto out; 4176b5162e19SFrançois Tigeot } 4177b5162e19SFrançois Tigeot 4178b5162e19SFrançois Tigeot props_count = obj->properties->count; 4179b5162e19SFrançois Tigeot 4180b5162e19SFrançois Tigeot /* This ioctl is called twice, once to determine how much space is 4181b5162e19SFrançois Tigeot * needed, and the 2nd time to fill it. */ 4182b5162e19SFrançois Tigeot if ((arg->count_props >= props_count) && props_count) { 4183b5162e19SFrançois Tigeot copied = 0; 4184b5162e19SFrançois Tigeot props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); 4185b5162e19SFrançois Tigeot prop_values_ptr = (uint64_t __user *)(unsigned long) 4186b5162e19SFrançois Tigeot (arg->prop_values_ptr); 4187b5162e19SFrançois Tigeot for (i = 0; i < props_count; i++) { 4188b5162e19SFrançois Tigeot if (put_user(obj->properties->ids[i], 4189b5162e19SFrançois Tigeot props_ptr + copied)) { 4190b5162e19SFrançois Tigeot ret = -EFAULT; 4191b5162e19SFrançois Tigeot goto out; 4192b5162e19SFrançois Tigeot } 4193b5162e19SFrançois Tigeot if (put_user(obj->properties->values[i], 4194b5162e19SFrançois Tigeot prop_values_ptr + copied)) { 4195b5162e19SFrançois Tigeot ret = -EFAULT; 4196b5162e19SFrançois Tigeot goto out; 4197b5162e19SFrançois Tigeot } 4198b5162e19SFrançois Tigeot copied++; 4199b5162e19SFrançois Tigeot } 4200b5162e19SFrançois Tigeot } 4201b5162e19SFrançois Tigeot arg->count_props = props_count; 4202b5162e19SFrançois Tigeot out: 42034dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 4204b5162e19SFrançois Tigeot return ret; 4205b5162e19SFrançois Tigeot } 4206b5162e19SFrançois Tigeot 4207ba55f2f5SFrançois Tigeot /** 4208ba55f2f5SFrançois Tigeot * drm_mode_obj_set_property_ioctl - set the current value of an object's property 4209ba55f2f5SFrançois Tigeot * @dev: DRM device 4210ba55f2f5SFrançois Tigeot * @data: ioctl data 4211ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4212ba55f2f5SFrançois Tigeot * 4213ba55f2f5SFrançois Tigeot * This function sets the current value for an object's property. It also calls 4214ba55f2f5SFrançois Tigeot * into a driver's ->set_property callback to update the hardware state. 4215ba55f2f5SFrançois Tigeot * Compared to the connector specific ioctl this one is extended to also work on 4216ba55f2f5SFrançois Tigeot * crtc and plane objects. 4217ba55f2f5SFrançois Tigeot * 4218ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4219ba55f2f5SFrançois Tigeot * 4220ba55f2f5SFrançois Tigeot * Returns: 4221ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4222ba55f2f5SFrançois Tigeot */ 4223b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, 4224b5162e19SFrançois Tigeot struct drm_file *file_priv) 4225b5162e19SFrançois Tigeot { 4226b5162e19SFrançois Tigeot struct drm_mode_obj_set_property *arg = data; 4227b5162e19SFrançois Tigeot struct drm_mode_object *arg_obj; 4228b5162e19SFrançois Tigeot struct drm_mode_object *prop_obj; 42295718399fSFrançois Tigeot struct drm_property *property; 42305718399fSFrançois Tigeot int ret = -EINVAL; 42315718399fSFrançois Tigeot int i; 42325718399fSFrançois Tigeot 42335718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 42345718399fSFrançois Tigeot return -EINVAL; 42355718399fSFrançois Tigeot 42364dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 42375718399fSFrançois Tigeot 4238b5162e19SFrançois Tigeot arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 42399edbd4a0SFrançois Tigeot if (!arg_obj) { 42409edbd4a0SFrançois Tigeot ret = -ENOENT; 42415718399fSFrançois Tigeot goto out; 42429edbd4a0SFrançois Tigeot } 4243b5162e19SFrançois Tigeot if (!arg_obj->properties) 4244b5162e19SFrançois Tigeot goto out; 42455718399fSFrançois Tigeot 4246b5162e19SFrançois Tigeot for (i = 0; i < arg_obj->properties->count; i++) 4247b5162e19SFrançois Tigeot if (arg_obj->properties->ids[i] == arg->prop_id) 4248b5162e19SFrançois Tigeot break; 4249b5162e19SFrançois Tigeot 4250b5162e19SFrançois Tigeot if (i == arg_obj->properties->count) 4251b5162e19SFrançois Tigeot goto out; 4252b5162e19SFrançois Tigeot 4253b5162e19SFrançois Tigeot prop_obj = drm_mode_object_find(dev, arg->prop_id, 4254b5162e19SFrançois Tigeot DRM_MODE_OBJECT_PROPERTY); 42559edbd4a0SFrançois Tigeot if (!prop_obj) { 42569edbd4a0SFrançois Tigeot ret = -ENOENT; 4257b5162e19SFrançois Tigeot goto out; 42589edbd4a0SFrançois Tigeot } 4259b5162e19SFrançois Tigeot property = obj_to_property(prop_obj); 4260b5162e19SFrançois Tigeot 4261b5162e19SFrançois Tigeot if (!drm_property_change_is_valid(property, arg->value)) 4262b5162e19SFrançois Tigeot goto out; 4263b5162e19SFrançois Tigeot 4264b5162e19SFrançois Tigeot switch (arg_obj->type) { 4265b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CONNECTOR: 4266b5162e19SFrançois Tigeot ret = drm_mode_connector_set_obj_prop(arg_obj, property, 4267b5162e19SFrançois Tigeot arg->value); 4268b5162e19SFrançois Tigeot break; 4269b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CRTC: 4270b5162e19SFrançois Tigeot ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); 4271b5162e19SFrançois Tigeot break; 4272b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_PLANE: 4273b5162e19SFrançois Tigeot ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); 42745718399fSFrançois Tigeot break; 42755718399fSFrançois Tigeot } 42765718399fSFrançois Tigeot 42775718399fSFrançois Tigeot out: 42784dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 42795718399fSFrançois Tigeot return ret; 42805718399fSFrançois Tigeot } 42815718399fSFrançois Tigeot 4282ba55f2f5SFrançois Tigeot /** 4283ba55f2f5SFrançois Tigeot * drm_mode_connector_attach_encoder - attach a connector to an encoder 4284ba55f2f5SFrançois Tigeot * @connector: connector to attach 4285ba55f2f5SFrançois Tigeot * @encoder: encoder to attach @connector to 4286ba55f2f5SFrançois Tigeot * 4287ba55f2f5SFrançois Tigeot * This function links up a connector to an encoder. Note that the routing 4288ba55f2f5SFrançois Tigeot * restrictions between encoders and crtcs are exposed to userspace through the 4289ba55f2f5SFrançois Tigeot * possible_clones and possible_crtcs bitmasks. 4290ba55f2f5SFrançois Tigeot * 4291ba55f2f5SFrançois Tigeot * Returns: 4292ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4293ba55f2f5SFrançois Tigeot */ 42945718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector, 42955718399fSFrançois Tigeot struct drm_encoder *encoder) 42965718399fSFrançois Tigeot { 42975718399fSFrançois Tigeot int i; 42985718399fSFrançois Tigeot 42995718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 43005718399fSFrançois Tigeot if (connector->encoder_ids[i] == 0) { 43015718399fSFrançois Tigeot connector->encoder_ids[i] = encoder->base.id; 43025718399fSFrançois Tigeot return 0; 43035718399fSFrançois Tigeot } 43045718399fSFrançois Tigeot } 43055718399fSFrançois Tigeot return -ENOMEM; 43065718399fSFrançois Tigeot } 4307b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder); 43085718399fSFrançois Tigeot 4309ba55f2f5SFrançois Tigeot /** 4310ba55f2f5SFrançois Tigeot * drm_mode_crtc_set_gamma_size - set the gamma table size 4311ba55f2f5SFrançois Tigeot * @crtc: CRTC to set the gamma table size for 4312ba55f2f5SFrançois Tigeot * @gamma_size: size of the gamma table 4313ba55f2f5SFrançois Tigeot * 4314ba55f2f5SFrançois Tigeot * Drivers which support gamma tables should set this to the supported gamma 4315ba55f2f5SFrançois Tigeot * table size when initializing the CRTC. Currently the drm core only supports a 4316ba55f2f5SFrançois Tigeot * fixed gamma table size. 4317ba55f2f5SFrançois Tigeot * 4318ba55f2f5SFrançois Tigeot * Returns: 4319ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4320ba55f2f5SFrançois Tigeot */ 43215718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, 43225718399fSFrançois Tigeot int gamma_size) 43235718399fSFrançois Tigeot { 43245718399fSFrançois Tigeot crtc->gamma_size = gamma_size; 43255718399fSFrançois Tigeot 43264dbb207bSFrançois Tigeot crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); 4327b5162e19SFrançois Tigeot if (!crtc->gamma_store) { 4328b5162e19SFrançois Tigeot crtc->gamma_size = 0; 4329b5162e19SFrançois Tigeot return -ENOMEM; 4330b5162e19SFrançois Tigeot } 43315718399fSFrançois Tigeot 43325718399fSFrançois Tigeot return 0; 43335718399fSFrançois Tigeot } 4334b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); 43355718399fSFrançois Tigeot 4336ba55f2f5SFrançois Tigeot /** 4337ba55f2f5SFrançois Tigeot * drm_mode_gamma_set_ioctl - set the gamma table 4338ba55f2f5SFrançois Tigeot * @dev: DRM device 4339ba55f2f5SFrançois Tigeot * @data: ioctl data 4340ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4341ba55f2f5SFrançois Tigeot * 4342ba55f2f5SFrançois Tigeot * Set the gamma table of a CRTC to the one passed in by the user. Userspace can 4343ba55f2f5SFrançois Tigeot * inquire the required gamma table size through drm_mode_gamma_get_ioctl. 4344ba55f2f5SFrançois Tigeot * 4345ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4346ba55f2f5SFrançois Tigeot * 4347ba55f2f5SFrançois Tigeot * Returns: 4348ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4349ba55f2f5SFrançois Tigeot */ 43505718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev, 43515718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 43525718399fSFrançois Tigeot { 43535718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 43545718399fSFrançois Tigeot struct drm_crtc *crtc; 43555718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 43565718399fSFrançois Tigeot int size; 43575718399fSFrançois Tigeot int ret = 0; 43585718399fSFrançois Tigeot 43595718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 43605718399fSFrançois Tigeot return -EINVAL; 43615718399fSFrançois Tigeot 43624dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 4363ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_lut->crtc_id); 4364ba55f2f5SFrançois Tigeot if (!crtc) { 43659edbd4a0SFrançois Tigeot ret = -ENOENT; 43665718399fSFrançois Tigeot goto out; 43675718399fSFrançois Tigeot } 43685718399fSFrançois Tigeot 4369b5162e19SFrançois Tigeot if (crtc->funcs->gamma_set == NULL) { 4370b5162e19SFrançois Tigeot ret = -ENOSYS; 4371b5162e19SFrançois Tigeot goto out; 4372b5162e19SFrançois Tigeot } 4373b5162e19SFrançois Tigeot 43745718399fSFrançois Tigeot /* memcpy into gamma store */ 43755718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 43765718399fSFrançois Tigeot ret = -EINVAL; 43775718399fSFrançois Tigeot goto out; 43785718399fSFrançois Tigeot } 43795718399fSFrançois Tigeot 43805718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 43815718399fSFrançois Tigeot r_base = crtc->gamma_store; 4382b5162e19SFrançois Tigeot if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { 43835718399fSFrançois Tigeot ret = -EFAULT; 43845718399fSFrançois Tigeot goto out; 43855718399fSFrançois Tigeot } 43865718399fSFrançois Tigeot 43875718399fSFrançois Tigeot g_base = (char *)r_base + size; 4388b5162e19SFrançois Tigeot if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { 43895718399fSFrançois Tigeot ret = -EFAULT; 43905718399fSFrançois Tigeot goto out; 43915718399fSFrançois Tigeot } 43925718399fSFrançois Tigeot 439306fede5aSFrançois Tigeot b_base = (char *)g_base + size; 4394b5162e19SFrançois Tigeot if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { 43955718399fSFrançois Tigeot ret = -EFAULT; 43965718399fSFrançois Tigeot goto out; 43975718399fSFrançois Tigeot } 43985718399fSFrançois Tigeot 43995718399fSFrançois Tigeot crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 44005718399fSFrançois Tigeot 44015718399fSFrançois Tigeot out: 44024dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 44035718399fSFrançois Tigeot return ret; 44045718399fSFrançois Tigeot 44055718399fSFrançois Tigeot } 44065718399fSFrançois Tigeot 4407ba55f2f5SFrançois Tigeot /** 4408ba55f2f5SFrançois Tigeot * drm_mode_gamma_get_ioctl - get the gamma table 4409ba55f2f5SFrançois Tigeot * @dev: DRM device 4410ba55f2f5SFrançois Tigeot * @data: ioctl data 4411ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4412ba55f2f5SFrançois Tigeot * 4413ba55f2f5SFrançois Tigeot * Copy the current gamma table into the storage provided. This also provides 4414ba55f2f5SFrançois Tigeot * the gamma table size the driver expects, which can be used to size the 4415ba55f2f5SFrançois Tigeot * allocated storage. 4416ba55f2f5SFrançois Tigeot * 4417ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4418ba55f2f5SFrançois Tigeot * 4419ba55f2f5SFrançois Tigeot * Returns: 4420ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4421ba55f2f5SFrançois Tigeot */ 44225718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev, 44235718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 44245718399fSFrançois Tigeot { 44255718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 44265718399fSFrançois Tigeot struct drm_crtc *crtc; 44275718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 44285718399fSFrançois Tigeot int size; 44295718399fSFrançois Tigeot int ret = 0; 44305718399fSFrançois Tigeot 44315718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 44325718399fSFrançois Tigeot return -EINVAL; 44335718399fSFrançois Tigeot 44344dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 4435ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_lut->crtc_id); 4436ba55f2f5SFrançois Tigeot if (!crtc) { 44379edbd4a0SFrançois Tigeot ret = -ENOENT; 44385718399fSFrançois Tigeot goto out; 44395718399fSFrançois Tigeot } 44405718399fSFrançois Tigeot 44415718399fSFrançois Tigeot /* memcpy into gamma store */ 44425718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 44435718399fSFrançois Tigeot ret = -EINVAL; 44445718399fSFrançois Tigeot goto out; 44455718399fSFrançois Tigeot } 44465718399fSFrançois Tigeot 44475718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 44485718399fSFrançois Tigeot r_base = crtc->gamma_store; 4449b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { 44505718399fSFrançois Tigeot ret = -EFAULT; 44515718399fSFrançois Tigeot goto out; 44525718399fSFrançois Tigeot } 44535718399fSFrançois Tigeot 44545718399fSFrançois Tigeot g_base = (char *)r_base + size; 4455b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { 44565718399fSFrançois Tigeot ret = -EFAULT; 44575718399fSFrançois Tigeot goto out; 44585718399fSFrançois Tigeot } 44595718399fSFrançois Tigeot 44605718399fSFrançois Tigeot b_base = (char *)g_base + size; 4461b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { 44625718399fSFrançois Tigeot ret = -EFAULT; 44635718399fSFrançois Tigeot goto out; 44645718399fSFrançois Tigeot } 44655718399fSFrançois Tigeot out: 44664dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 44675718399fSFrançois Tigeot return ret; 44685718399fSFrançois Tigeot } 44695718399fSFrançois Tigeot 44700554239fSFrançois Tigeot /* 44710554239fSFrançois Tigeot * The Linux version of kfree() is a macro and can't be called 44720554239fSFrançois Tigeot * directly via a function pointer 44730554239fSFrançois Tigeot */ 44740554239fSFrançois Tigeot static void drm_kms_free(void *arg) 44750554239fSFrançois Tigeot { 44760554239fSFrançois Tigeot kfree(arg); 44770554239fSFrançois Tigeot } 44780554239fSFrançois Tigeot 4479ba55f2f5SFrançois Tigeot /** 4480ba55f2f5SFrançois Tigeot * drm_mode_page_flip_ioctl - schedule an asynchronous fb update 4481ba55f2f5SFrançois Tigeot * @dev: DRM device 4482ba55f2f5SFrançois Tigeot * @data: ioctl data 4483ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4484ba55f2f5SFrançois Tigeot * 4485ba55f2f5SFrançois Tigeot * This schedules an asynchronous update on a given CRTC, called page flip. 4486ba55f2f5SFrançois Tigeot * Optionally a drm event is generated to signal the completion of the event. 4487ba55f2f5SFrançois Tigeot * Generic drivers cannot assume that a pageflip with changed framebuffer 4488ba55f2f5SFrançois Tigeot * properties (including driver specific metadata like tiling layout) will work, 4489ba55f2f5SFrançois Tigeot * but some drivers support e.g. pixel format changes through the pageflip 4490ba55f2f5SFrançois Tigeot * ioctl. 4491ba55f2f5SFrançois Tigeot * 4492ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4493ba55f2f5SFrançois Tigeot * 4494ba55f2f5SFrançois Tigeot * Returns: 4495ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4496a5f72378SFrançois Tigeot */ 4497b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev, 4498b5162e19SFrançois Tigeot void *data, struct drm_file *file_priv) 44995718399fSFrançois Tigeot { 45005718399fSFrançois Tigeot struct drm_mode_crtc_page_flip *page_flip = data; 45015718399fSFrançois Tigeot struct drm_crtc *crtc; 45024dbb207bSFrançois Tigeot struct drm_framebuffer *fb = NULL, *old_fb = NULL; 45035718399fSFrançois Tigeot struct drm_pending_vblank_event *e = NULL; 4504b5162e19SFrançois Tigeot int ret = -EINVAL; 45055718399fSFrançois Tigeot 45065718399fSFrançois Tigeot if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || 45075718399fSFrançois Tigeot page_flip->reserved != 0) 4508b5162e19SFrançois Tigeot return -EINVAL; 45095718399fSFrançois Tigeot 45109edbd4a0SFrançois Tigeot if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) 45119edbd4a0SFrançois Tigeot return -EINVAL; 45129edbd4a0SFrançois Tigeot 4513ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, page_flip->crtc_id); 4514ba55f2f5SFrançois Tigeot if (!crtc) 45159edbd4a0SFrançois Tigeot return -ENOENT; 45165718399fSFrançois Tigeot 4517ba55f2f5SFrançois Tigeot drm_modeset_lock(&crtc->mutex, NULL); 4518ba55f2f5SFrançois Tigeot if (crtc->primary->fb == NULL) { 45195718399fSFrançois Tigeot /* The framebuffer is currently unbound, presumably 45205718399fSFrançois Tigeot * due to a hotplug event, that userspace has not 45215718399fSFrançois Tigeot * yet discovered. 45225718399fSFrançois Tigeot */ 4523b5162e19SFrançois Tigeot ret = -EBUSY; 45245718399fSFrançois Tigeot goto out; 45255718399fSFrançois Tigeot } 45265718399fSFrançois Tigeot 45275718399fSFrançois Tigeot if (crtc->funcs->page_flip == NULL) 45285718399fSFrançois Tigeot goto out; 45295718399fSFrançois Tigeot 45304dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, page_flip->fb_id); 45319edbd4a0SFrançois Tigeot if (!fb) { 45329edbd4a0SFrançois Tigeot ret = -ENOENT; 45335718399fSFrançois Tigeot goto out; 45345718399fSFrançois Tigeot } 45355718399fSFrançois Tigeot 45369edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); 45379edbd4a0SFrançois Tigeot if (ret) 45389edbd4a0SFrançois Tigeot goto out; 45399edbd4a0SFrançois Tigeot 4540ba55f2f5SFrançois Tigeot if (crtc->primary->fb->pixel_format != fb->pixel_format) { 45414dbb207bSFrançois Tigeot DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); 45424dbb207bSFrançois Tigeot ret = -EINVAL; 45434dbb207bSFrançois Tigeot goto out; 45444dbb207bSFrançois Tigeot } 45454dbb207bSFrançois Tigeot 45465718399fSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 4547b5162e19SFrançois Tigeot ret = -ENOMEM; 45485718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 45495718399fSFrançois Tigeot if (file_priv->event_space < sizeof e->event) { 45505718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 45515718399fSFrançois Tigeot goto out; 45525718399fSFrançois Tigeot } 45535718399fSFrançois Tigeot file_priv->event_space -= sizeof e->event; 45545718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 45555718399fSFrançois Tigeot 45564dbb207bSFrançois Tigeot e = kzalloc(sizeof *e, GFP_KERNEL); 4557b5162e19SFrançois Tigeot if (e == NULL) { 4558b5162e19SFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 4559b5162e19SFrançois Tigeot file_priv->event_space += sizeof e->event; 4560b5162e19SFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 4561b5162e19SFrançois Tigeot goto out; 4562b5162e19SFrançois Tigeot } 45635718399fSFrançois Tigeot 45645718399fSFrançois Tigeot e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 45655718399fSFrançois Tigeot e->event.base.length = sizeof e->event; 45665718399fSFrançois Tigeot e->event.user_data = page_flip->user_data; 45675718399fSFrançois Tigeot e->base.event = &e->event.base; 45685718399fSFrançois Tigeot e->base.file_priv = file_priv; 45690554239fSFrançois Tigeot /* XXX: DragonFly-specific */ 45705718399fSFrançois Tigeot e->base.destroy = 45710554239fSFrançois Tigeot (void (*) (struct drm_pending_event *)) drm_kms_free; 45725718399fSFrançois Tigeot } 45735718399fSFrançois Tigeot 4574ba55f2f5SFrançois Tigeot old_fb = crtc->primary->fb; 45759edbd4a0SFrançois Tigeot ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); 4576b5162e19SFrançois Tigeot if (ret) { 45775718399fSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 45785718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 45795718399fSFrançois Tigeot file_priv->event_space += sizeof e->event; 45805718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 45814dbb207bSFrançois Tigeot kfree(e); 45825718399fSFrançois Tigeot } 45834dbb207bSFrançois Tigeot /* Keep the old fb, don't unref it. */ 45844dbb207bSFrançois Tigeot old_fb = NULL; 45854dbb207bSFrançois Tigeot } else { 45864dbb207bSFrançois Tigeot /* 45874dbb207bSFrançois Tigeot * Warn if the driver hasn't properly updated the crtc->fb 45884dbb207bSFrançois Tigeot * field to reflect that the new framebuffer is now used. 45894dbb207bSFrançois Tigeot * Failing to do so will screw with the reference counting 45904dbb207bSFrançois Tigeot * on framebuffers. 45914dbb207bSFrançois Tigeot */ 4592ba55f2f5SFrançois Tigeot WARN_ON(crtc->primary->fb != fb); 45934dbb207bSFrançois Tigeot /* Unref only the old framebuffer. */ 45944dbb207bSFrançois Tigeot fb = NULL; 45955718399fSFrançois Tigeot } 45965718399fSFrançois Tigeot 45975718399fSFrançois Tigeot out: 45984dbb207bSFrançois Tigeot if (fb) 45994dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 46004dbb207bSFrançois Tigeot if (old_fb) 46014dbb207bSFrançois Tigeot drm_framebuffer_unreference(old_fb); 4602ba55f2f5SFrançois Tigeot drm_modeset_unlock(&crtc->mutex); 46034dbb207bSFrançois Tigeot 4604b5162e19SFrançois Tigeot return ret; 46055718399fSFrançois Tigeot } 46065718399fSFrançois Tigeot 4607ba55f2f5SFrançois Tigeot /** 4608ba55f2f5SFrançois Tigeot * drm_mode_config_reset - call ->reset callbacks 4609ba55f2f5SFrançois Tigeot * @dev: drm device 4610ba55f2f5SFrançois Tigeot * 4611ba55f2f5SFrançois Tigeot * This functions calls all the crtc's, encoder's and connector's ->reset 4612ba55f2f5SFrançois Tigeot * callback. Drivers can use this in e.g. their driver load or resume code to 4613ba55f2f5SFrançois Tigeot * reset hardware and software state. 4614ba55f2f5SFrançois Tigeot */ 46155718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev) 46165718399fSFrançois Tigeot { 46175718399fSFrançois Tigeot struct drm_crtc *crtc; 46185718399fSFrançois Tigeot struct drm_encoder *encoder; 46195718399fSFrançois Tigeot struct drm_connector *connector; 46205718399fSFrançois Tigeot 46215718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 46225718399fSFrançois Tigeot if (crtc->funcs->reset) 46235718399fSFrançois Tigeot crtc->funcs->reset(crtc); 46245718399fSFrançois Tigeot 46255718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 46265718399fSFrançois Tigeot if (encoder->funcs->reset) 46275718399fSFrançois Tigeot encoder->funcs->reset(encoder); 46285718399fSFrançois Tigeot 4629b5162e19SFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 4630b5162e19SFrançois Tigeot connector->status = connector_status_unknown; 4631b5162e19SFrançois Tigeot 46325718399fSFrançois Tigeot if (connector->funcs->reset) 46335718399fSFrançois Tigeot connector->funcs->reset(connector); 46345718399fSFrançois Tigeot } 4635b5162e19SFrançois Tigeot } 4636b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset); 46375718399fSFrançois Tigeot 4638ba55f2f5SFrançois Tigeot /** 4639ba55f2f5SFrançois Tigeot * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer 4640ba55f2f5SFrançois Tigeot * @dev: DRM device 4641ba55f2f5SFrançois Tigeot * @data: ioctl data 4642ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4643ba55f2f5SFrançois Tigeot * 4644ba55f2f5SFrançois Tigeot * This creates a new dumb buffer in the driver's backing storage manager (GEM, 4645ba55f2f5SFrançois Tigeot * TTM or something else entirely) and returns the resulting buffer handle. This 4646ba55f2f5SFrançois Tigeot * handle can then be wrapped up into a framebuffer modeset object. 4647ba55f2f5SFrançois Tigeot * 4648ba55f2f5SFrançois Tigeot * Note that userspace is not allowed to use such objects for render 4649ba55f2f5SFrançois Tigeot * acceleration - drivers must create their own private ioctls for such a use 4650ba55f2f5SFrançois Tigeot * case. 4651ba55f2f5SFrançois Tigeot * 4652ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4653ba55f2f5SFrançois Tigeot * 4654ba55f2f5SFrançois Tigeot * Returns: 4655ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4656ba55f2f5SFrançois Tigeot */ 46575718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev, 46585718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 46595718399fSFrançois Tigeot { 46605718399fSFrançois Tigeot struct drm_mode_create_dumb *args = data; 4661ba55f2f5SFrançois Tigeot u32 cpp, stride, size; 46625718399fSFrançois Tigeot 46635718399fSFrançois Tigeot if (!dev->driver->dumb_create) 4664b5162e19SFrançois Tigeot return -ENOSYS; 4665ba55f2f5SFrançois Tigeot if (!args->width || !args->height || !args->bpp) 4666ba55f2f5SFrançois Tigeot return -EINVAL; 4667ba55f2f5SFrançois Tigeot 4668ba55f2f5SFrançois Tigeot /* overflow checks for 32bit size calculations */ 4669*24edb884SFrançois Tigeot /* NOTE: DIV_ROUND_UP() can overflow */ 4670ba55f2f5SFrançois Tigeot cpp = DIV_ROUND_UP(args->bpp, 8); 4671*24edb884SFrançois Tigeot if (!cpp || cpp > 0xffffffffU / args->width) 4672ba55f2f5SFrançois Tigeot return -EINVAL; 4673ba55f2f5SFrançois Tigeot stride = cpp * args->width; 4674ba55f2f5SFrançois Tigeot if (args->height > 0xffffffffU / stride) 4675ba55f2f5SFrançois Tigeot return -EINVAL; 4676ba55f2f5SFrançois Tigeot 4677ba55f2f5SFrançois Tigeot /* test for wrap-around */ 4678ba55f2f5SFrançois Tigeot size = args->height * stride; 4679ba55f2f5SFrançois Tigeot if (PAGE_ALIGN(size) == 0) 4680ba55f2f5SFrançois Tigeot return -EINVAL; 4681ba55f2f5SFrançois Tigeot 46825718399fSFrançois Tigeot return dev->driver->dumb_create(file_priv, dev, args); 46835718399fSFrançois Tigeot } 46845718399fSFrançois Tigeot 4685ba55f2f5SFrançois Tigeot /** 4686ba55f2f5SFrançois Tigeot * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer 4687ba55f2f5SFrançois Tigeot * @dev: DRM device 4688ba55f2f5SFrançois Tigeot * @data: ioctl data 4689ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4690ba55f2f5SFrançois Tigeot * 4691ba55f2f5SFrançois Tigeot * Allocate an offset in the drm device node's address space to be able to 4692ba55f2f5SFrançois Tigeot * memory map a dumb buffer. 4693ba55f2f5SFrançois Tigeot * 4694ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4695ba55f2f5SFrançois Tigeot * 4696ba55f2f5SFrançois Tigeot * Returns: 4697ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4698ba55f2f5SFrançois Tigeot */ 46995718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, 47005718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 47015718399fSFrançois Tigeot { 47025718399fSFrançois Tigeot struct drm_mode_map_dumb *args = data; 47035718399fSFrançois Tigeot 47045718399fSFrançois Tigeot /* call driver ioctl to get mmap offset */ 47055718399fSFrançois Tigeot if (!dev->driver->dumb_map_offset) 4706b5162e19SFrançois Tigeot return -ENOSYS; 47075718399fSFrançois Tigeot 47085718399fSFrançois Tigeot return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); 47095718399fSFrançois Tigeot } 47105718399fSFrançois Tigeot 4711ba55f2f5SFrançois Tigeot /** 4712ba55f2f5SFrançois Tigeot * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer 4713ba55f2f5SFrançois Tigeot * @dev: DRM device 4714ba55f2f5SFrançois Tigeot * @data: ioctl data 4715ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4716ba55f2f5SFrançois Tigeot * 4717ba55f2f5SFrançois Tigeot * This destroys the userspace handle for the given dumb backing storage buffer. 4718ba55f2f5SFrançois Tigeot * Since buffer objects must be reference counted in the kernel a buffer object 4719ba55f2f5SFrançois Tigeot * won't be immediately freed if a framebuffer modeset object still uses it. 4720ba55f2f5SFrançois Tigeot * 4721ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4722ba55f2f5SFrançois Tigeot * 4723ba55f2f5SFrançois Tigeot * Returns: 4724ba55f2f5SFrançois Tigeot * Zero on success, errno on failure. 4725ba55f2f5SFrançois Tigeot */ 47265718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, 47275718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 47285718399fSFrançois Tigeot { 47295718399fSFrançois Tigeot struct drm_mode_destroy_dumb *args = data; 47305718399fSFrançois Tigeot 47315718399fSFrançois Tigeot if (!dev->driver->dumb_destroy) 4732b5162e19SFrançois Tigeot return -ENOSYS; 47335718399fSFrançois Tigeot 47345718399fSFrançois Tigeot return dev->driver->dumb_destroy(file_priv, dev, args->handle); 47355718399fSFrançois Tigeot } 47365718399fSFrançois Tigeot 4737ba55f2f5SFrançois Tigeot /** 4738ba55f2f5SFrançois Tigeot * drm_fb_get_bpp_depth - get the bpp/depth values for format 4739ba55f2f5SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 4740ba55f2f5SFrançois Tigeot * @depth: storage for the depth value 4741ba55f2f5SFrançois Tigeot * @bpp: storage for the bpp value 4742ba55f2f5SFrançois Tigeot * 4743ba55f2f5SFrançois Tigeot * This only supports RGB formats here for compat with code that doesn't use 4744ba55f2f5SFrançois Tigeot * pixel formats directly yet. 47455718399fSFrançois Tigeot */ 47465718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, 47475718399fSFrançois Tigeot int *bpp) 47485718399fSFrançois Tigeot { 47495718399fSFrançois Tigeot switch (format) { 47504dbb207bSFrançois Tigeot case DRM_FORMAT_C8: 47515718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 47525718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 47535718399fSFrançois Tigeot *depth = 8; 47545718399fSFrançois Tigeot *bpp = 8; 47555718399fSFrançois Tigeot break; 47565718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 47575718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 47585718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 47595718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 47605718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 47615718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 47625718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 47635718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 47645718399fSFrançois Tigeot *depth = 15; 47655718399fSFrançois Tigeot *bpp = 16; 47665718399fSFrançois Tigeot break; 47675718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 47685718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 47695718399fSFrançois Tigeot *depth = 16; 47705718399fSFrançois Tigeot *bpp = 16; 47715718399fSFrançois Tigeot break; 47725718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 47735718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 47745718399fSFrançois Tigeot *depth = 24; 47755718399fSFrançois Tigeot *bpp = 24; 47765718399fSFrançois Tigeot break; 47775718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 47785718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 47795718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 47805718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 47815718399fSFrançois Tigeot *depth = 24; 47825718399fSFrançois Tigeot *bpp = 32; 47835718399fSFrançois Tigeot break; 47845718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 47855718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 47865718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 47875718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 47885718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 47895718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 47905718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 47915718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 47925718399fSFrançois Tigeot *depth = 30; 47935718399fSFrançois Tigeot *bpp = 32; 47945718399fSFrançois Tigeot break; 47955718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 47965718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 47975718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 47985718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 47995718399fSFrançois Tigeot *depth = 32; 48005718399fSFrançois Tigeot *bpp = 32; 48015718399fSFrançois Tigeot break; 48025718399fSFrançois Tigeot default: 48039edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("unsupported pixel format %s\n", 48049edbd4a0SFrançois Tigeot drm_get_format_name(format)); 48055718399fSFrançois Tigeot *depth = 0; 48065718399fSFrançois Tigeot *bpp = 0; 48075718399fSFrançois Tigeot break; 48085718399fSFrançois Tigeot } 48095718399fSFrançois Tigeot } 4810b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth); 4811b5162e19SFrançois Tigeot 4812b5162e19SFrançois Tigeot /** 4813b5162e19SFrançois Tigeot * drm_format_num_planes - get the number of planes for format 4814b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 4815b5162e19SFrançois Tigeot * 4816ba55f2f5SFrançois Tigeot * Returns: 4817b5162e19SFrançois Tigeot * The number of planes used by the specified pixel format. 4818b5162e19SFrançois Tigeot */ 4819b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format) 4820b5162e19SFrançois Tigeot { 4821b5162e19SFrançois Tigeot switch (format) { 4822b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 4823b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 4824b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 4825b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 4826b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 4827b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 4828b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 4829b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 4830b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 4831b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 4832b5162e19SFrançois Tigeot return 3; 4833b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 4834b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 4835b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 4836b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 4837b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 4838b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 4839b5162e19SFrançois Tigeot return 2; 4840b5162e19SFrançois Tigeot default: 4841b5162e19SFrançois Tigeot return 1; 4842b5162e19SFrançois Tigeot } 4843b5162e19SFrançois Tigeot } 4844b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes); 4845b5162e19SFrançois Tigeot 4846b5162e19SFrançois Tigeot /** 4847b5162e19SFrançois Tigeot * drm_format_plane_cpp - determine the bytes per pixel value 4848b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 4849b5162e19SFrançois Tigeot * @plane: plane index 4850b5162e19SFrançois Tigeot * 4851ba55f2f5SFrançois Tigeot * Returns: 4852b5162e19SFrançois Tigeot * The bytes per pixel value for the specified plane. 4853b5162e19SFrançois Tigeot */ 4854b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane) 4855b5162e19SFrançois Tigeot { 4856b5162e19SFrançois Tigeot unsigned int depth; 4857b5162e19SFrançois Tigeot int bpp; 4858b5162e19SFrançois Tigeot 4859b5162e19SFrançois Tigeot if (plane >= drm_format_num_planes(format)) 4860b5162e19SFrançois Tigeot return 0; 4861b5162e19SFrançois Tigeot 4862b5162e19SFrançois Tigeot switch (format) { 4863b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 4864b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 4865b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 4866b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 4867b5162e19SFrançois Tigeot return 2; 4868b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 4869b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 4870b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 4871b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 4872b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 4873b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 4874b5162e19SFrançois Tigeot return plane ? 2 : 1; 4875b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 4876b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 4877b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 4878b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 4879b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 4880b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 4881b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 4882b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 4883b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 4884b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 4885b5162e19SFrançois Tigeot return 1; 4886b5162e19SFrançois Tigeot default: 4887b5162e19SFrançois Tigeot drm_fb_get_bpp_depth(format, &depth, &bpp); 4888b5162e19SFrançois Tigeot return bpp >> 3; 4889b5162e19SFrançois Tigeot } 4890b5162e19SFrançois Tigeot } 4891b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp); 4892b5162e19SFrançois Tigeot 4893b5162e19SFrançois Tigeot /** 4894b5162e19SFrançois Tigeot * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor 4895b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 4896b5162e19SFrançois Tigeot * 4897ba55f2f5SFrançois Tigeot * Returns: 4898b5162e19SFrançois Tigeot * The horizontal chroma subsampling factor for the 4899b5162e19SFrançois Tigeot * specified pixel format. 4900b5162e19SFrançois Tigeot */ 4901b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format) 4902b5162e19SFrançois Tigeot { 4903b5162e19SFrançois Tigeot switch (format) { 4904b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 4905b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 4906b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 4907b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 4908b5162e19SFrançois Tigeot return 4; 4909b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 4910b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 4911b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 4912b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 4913b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 4914b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 4915b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 4916b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 4917b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 4918b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 4919b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 4920b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 4921b5162e19SFrançois Tigeot return 2; 4922b5162e19SFrançois Tigeot default: 4923b5162e19SFrançois Tigeot return 1; 4924b5162e19SFrançois Tigeot } 4925b5162e19SFrançois Tigeot } 4926b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); 4927b5162e19SFrançois Tigeot 4928b5162e19SFrançois Tigeot /** 4929b5162e19SFrançois Tigeot * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor 4930b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 4931b5162e19SFrançois Tigeot * 4932ba55f2f5SFrançois Tigeot * Returns: 4933b5162e19SFrançois Tigeot * The vertical chroma subsampling factor for the 4934b5162e19SFrançois Tigeot * specified pixel format. 4935b5162e19SFrançois Tigeot */ 4936b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format) 4937b5162e19SFrançois Tigeot { 4938b5162e19SFrançois Tigeot switch (format) { 4939b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 4940b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 4941b5162e19SFrançois Tigeot return 4; 4942b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 4943b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 4944b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 4945b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 4946b5162e19SFrançois Tigeot return 2; 4947b5162e19SFrançois Tigeot default: 4948b5162e19SFrançois Tigeot return 1; 4949b5162e19SFrançois Tigeot } 4950b5162e19SFrançois Tigeot } 4951b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); 49524dbb207bSFrançois Tigeot 49534dbb207bSFrançois Tigeot /** 49544dbb207bSFrançois Tigeot * drm_mode_config_init - initialize DRM mode_configuration structure 49554dbb207bSFrançois Tigeot * @dev: DRM device 49564dbb207bSFrançois Tigeot * 49574dbb207bSFrançois Tigeot * Initialize @dev's mode_config structure, used for tracking the graphics 49584dbb207bSFrançois Tigeot * configuration of @dev. 49594dbb207bSFrançois Tigeot * 49604dbb207bSFrançois Tigeot * Since this initializes the modeset locks, no locking is possible. Which is no 49614dbb207bSFrançois Tigeot * problem, since this should happen single threaded at init time. It is the 49624dbb207bSFrançois Tigeot * driver's problem to ensure this guarantee. 49634dbb207bSFrançois Tigeot * 49644dbb207bSFrançois Tigeot */ 49654dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev) 49664dbb207bSFrançois Tigeot { 49674dbb207bSFrançois Tigeot lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE); 4968ba55f2f5SFrançois Tigeot drm_modeset_lock_init(&dev->mode_config.connection_mutex); 49694dbb207bSFrançois Tigeot lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE); 49704dbb207bSFrançois Tigeot lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE); 49714dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.fb_list); 49724dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.crtc_list); 49734dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.connector_list); 4974ba55f2f5SFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.bridge_list); 49754dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.encoder_list); 49764dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_list); 49774dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_blob_list); 49784dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.plane_list); 49794dbb207bSFrançois Tigeot idr_init(&dev->mode_config.crtc_idr); 49804dbb207bSFrançois Tigeot 49814dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 49824dbb207bSFrançois Tigeot drm_mode_create_standard_connector_properties(dev); 4983ba55f2f5SFrançois Tigeot drm_mode_create_standard_plane_properties(dev); 49844dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 49854dbb207bSFrançois Tigeot 49864dbb207bSFrançois Tigeot /* Just to be sure */ 49874dbb207bSFrançois Tigeot dev->mode_config.num_fb = 0; 49884dbb207bSFrançois Tigeot dev->mode_config.num_connector = 0; 49894dbb207bSFrançois Tigeot dev->mode_config.num_crtc = 0; 49904dbb207bSFrançois Tigeot dev->mode_config.num_encoder = 0; 4991ba55f2f5SFrançois Tigeot dev->mode_config.num_overlay_plane = 0; 4992ba55f2f5SFrançois Tigeot dev->mode_config.num_total_plane = 0; 49934dbb207bSFrançois Tigeot } 49944dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init); 49954dbb207bSFrançois Tigeot 49964dbb207bSFrançois Tigeot /** 49974dbb207bSFrançois Tigeot * drm_mode_config_cleanup - free up DRM mode_config info 49984dbb207bSFrançois Tigeot * @dev: DRM device 49994dbb207bSFrançois Tigeot * 50004dbb207bSFrançois Tigeot * Free up all the connectors and CRTCs associated with this DRM device, then 50014dbb207bSFrançois Tigeot * free up the framebuffers and associated buffer objects. 50024dbb207bSFrançois Tigeot * 50034dbb207bSFrançois Tigeot * Note that since this /should/ happen single-threaded at driver/device 50044dbb207bSFrançois Tigeot * teardown time, no locking is required. It's the driver's job to ensure that 50054dbb207bSFrançois Tigeot * this guarantee actually holds true. 50064dbb207bSFrançois Tigeot * 50074dbb207bSFrançois Tigeot * FIXME: cleanup any dangling user buffer objects too 50084dbb207bSFrançois Tigeot */ 50094dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev) 50104dbb207bSFrançois Tigeot { 50114dbb207bSFrançois Tigeot struct drm_connector *connector, *ot; 50124dbb207bSFrançois Tigeot struct drm_crtc *crtc, *ct; 50134dbb207bSFrançois Tigeot struct drm_encoder *encoder, *enct; 5014ba55f2f5SFrançois Tigeot struct drm_bridge *bridge, *brt; 50154dbb207bSFrançois Tigeot struct drm_framebuffer *fb, *fbt; 50164dbb207bSFrançois Tigeot struct drm_property *property, *pt; 50174dbb207bSFrançois Tigeot struct drm_property_blob *blob, *bt; 50184dbb207bSFrançois Tigeot struct drm_plane *plane, *plt; 50194dbb207bSFrançois Tigeot 50204dbb207bSFrançois Tigeot list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, 50214dbb207bSFrançois Tigeot head) { 50224dbb207bSFrançois Tigeot encoder->funcs->destroy(encoder); 50234dbb207bSFrançois Tigeot } 50244dbb207bSFrançois Tigeot 5025ba55f2f5SFrançois Tigeot list_for_each_entry_safe(bridge, brt, 5026ba55f2f5SFrançois Tigeot &dev->mode_config.bridge_list, head) { 5027ba55f2f5SFrançois Tigeot bridge->funcs->destroy(bridge); 5028ba55f2f5SFrançois Tigeot } 5029ba55f2f5SFrançois Tigeot 50304dbb207bSFrançois Tigeot list_for_each_entry_safe(connector, ot, 50314dbb207bSFrançois Tigeot &dev->mode_config.connector_list, head) { 50324dbb207bSFrançois Tigeot connector->funcs->destroy(connector); 50334dbb207bSFrançois Tigeot } 50344dbb207bSFrançois Tigeot 50354dbb207bSFrançois Tigeot list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, 50364dbb207bSFrançois Tigeot head) { 50374dbb207bSFrançois Tigeot drm_property_destroy(dev, property); 50384dbb207bSFrançois Tigeot } 50394dbb207bSFrançois Tigeot 50404dbb207bSFrançois Tigeot list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, 50414dbb207bSFrançois Tigeot head) { 50424dbb207bSFrançois Tigeot drm_property_destroy_blob(dev, blob); 50434dbb207bSFrançois Tigeot } 50444dbb207bSFrançois Tigeot 50454dbb207bSFrançois Tigeot /* 50464dbb207bSFrançois Tigeot * Single-threaded teardown context, so it's not required to grab the 50474dbb207bSFrançois Tigeot * fb_lock to protect against concurrent fb_list access. Contrary, it 50484dbb207bSFrançois Tigeot * would actually deadlock with the drm_framebuffer_cleanup function. 50494dbb207bSFrançois Tigeot * 50504dbb207bSFrançois Tigeot * Also, if there are any framebuffers left, that's a driver leak now, 50514dbb207bSFrançois Tigeot * so politely WARN about this. 50524dbb207bSFrançois Tigeot */ 50534dbb207bSFrançois Tigeot WARN_ON(!list_empty(&dev->mode_config.fb_list)); 50544dbb207bSFrançois Tigeot list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 50554dbb207bSFrançois Tigeot drm_framebuffer_remove(fb); 50564dbb207bSFrançois Tigeot } 50574dbb207bSFrançois Tigeot 50584dbb207bSFrançois Tigeot list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 50594dbb207bSFrançois Tigeot head) { 50604dbb207bSFrançois Tigeot plane->funcs->destroy(plane); 50614dbb207bSFrançois Tigeot } 50624dbb207bSFrançois Tigeot 50634dbb207bSFrançois Tigeot list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { 50644dbb207bSFrançois Tigeot crtc->funcs->destroy(crtc); 50654dbb207bSFrançois Tigeot } 50664dbb207bSFrançois Tigeot 50674dbb207bSFrançois Tigeot idr_destroy(&dev->mode_config.crtc_idr); 5068ba55f2f5SFrançois Tigeot drm_modeset_lock_fini(&dev->mode_config.connection_mutex); 50694dbb207bSFrançois Tigeot } 50704dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup); 5071