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> 344dbb207bSFrançois Tigeot #include <linux/slab.h> 35b5162e19SFrançois Tigeot #include <linux/export.h> 3618e26a6dSFrançois Tigeot #include <drm/drmP.h> 3718e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3818e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 39b5162e19SFrançois Tigeot #include <uapi_drm/drm_fourcc.h> 405718399fSFrançois Tigeot 41a2fdbec6SFrançois Tigeot /** 42a2fdbec6SFrançois Tigeot * drm_modeset_lock_all - take all modeset locks 43a2fdbec6SFrançois Tigeot * @dev: drm device 44a2fdbec6SFrançois Tigeot * 45a2fdbec6SFrançois Tigeot * This function takes all modeset locks, suitable where a more fine-grained 46a2fdbec6SFrançois Tigeot * scheme isn't (yet) implemented. 47a2fdbec6SFrançois Tigeot */ 48a2fdbec6SFrançois Tigeot void drm_modeset_lock_all(struct drm_device *dev) 49a2fdbec6SFrançois Tigeot { 50a2fdbec6SFrançois Tigeot struct drm_crtc *crtc; 51a2fdbec6SFrançois Tigeot 524dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 53a2fdbec6SFrançois Tigeot 54a2fdbec6SFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 55a2fdbec6SFrançois Tigeot lockmgr(&crtc->mutex, LK_EXCLUSIVE); 56a2fdbec6SFrançois Tigeot } 57a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_lock_all); 58a2fdbec6SFrançois Tigeot 59a2fdbec6SFrançois Tigeot /** 60a2fdbec6SFrançois Tigeot * drm_modeset_unlock_all - drop all modeset locks 61a2fdbec6SFrançois Tigeot * @dev: device 62a2fdbec6SFrançois Tigeot */ 63a2fdbec6SFrançois Tigeot void drm_modeset_unlock_all(struct drm_device *dev) 64a2fdbec6SFrançois Tigeot { 65a2fdbec6SFrançois Tigeot struct drm_crtc *crtc; 66a2fdbec6SFrançois Tigeot 67a2fdbec6SFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 684dbb207bSFrançois Tigeot mutex_unlock(&crtc->mutex); 69a2fdbec6SFrançois Tigeot 704dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 71a2fdbec6SFrançois Tigeot } 72a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_modeset_unlock_all); 73a2fdbec6SFrançois Tigeot 744dbb207bSFrançois Tigeot /** 754dbb207bSFrançois Tigeot * drm_warn_on_modeset_not_all_locked - check that all modeset locks are locked 764dbb207bSFrançois Tigeot * @dev: device 774dbb207bSFrançois Tigeot */ 784dbb207bSFrançois Tigeot void drm_warn_on_modeset_not_all_locked(struct drm_device *dev) 794dbb207bSFrançois Tigeot { 804dbb207bSFrançois Tigeot struct drm_crtc *crtc; 814dbb207bSFrançois Tigeot 824dbb207bSFrançois Tigeot /* Locking is currently fubar in the panic handler. */ 83*9edbd4a0SFrançois Tigeot #if 0 844dbb207bSFrançois Tigeot if (oops_in_progress) 854dbb207bSFrançois Tigeot return; 864dbb207bSFrançois Tigeot #endif 874dbb207bSFrançois Tigeot 884dbb207bSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 894dbb207bSFrançois Tigeot WARN_ON(!mutex_is_locked(&crtc->mutex)); 904dbb207bSFrançois Tigeot 914dbb207bSFrançois Tigeot WARN_ON(!mutex_is_locked(&dev->mode_config.mutex)); 924dbb207bSFrançois Tigeot } 934dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_warn_on_modeset_not_all_locked); 944dbb207bSFrançois Tigeot 955718399fSFrançois Tigeot /* Avoid boilerplate. I'm tired of typing. */ 965718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list) \ 9706fede5aSFrançois Tigeot const char *fnname(int val) \ 985718399fSFrançois Tigeot { \ 995718399fSFrançois Tigeot int i; \ 100b5162e19SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(list); i++) { \ 1015718399fSFrançois Tigeot if (list[i].type == val) \ 1025718399fSFrançois Tigeot return list[i].name; \ 1035718399fSFrançois Tigeot } \ 1045718399fSFrançois Tigeot return "(unknown)"; \ 1055718399fSFrançois Tigeot } 1065718399fSFrançois Tigeot 1075718399fSFrançois Tigeot /* 1085718399fSFrançois Tigeot * Global properties 1095718399fSFrançois Tigeot */ 11006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] = 1115718399fSFrançois Tigeot { { DRM_MODE_DPMS_ON, "On" }, 1125718399fSFrançois Tigeot { DRM_MODE_DPMS_STANDBY, "Standby" }, 1135718399fSFrançois Tigeot { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 1145718399fSFrançois Tigeot { DRM_MODE_DPMS_OFF, "Off" } 1155718399fSFrançois Tigeot }; 1165718399fSFrançois Tigeot 1175718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 1185718399fSFrançois Tigeot 1195718399fSFrançois Tigeot /* 1205718399fSFrançois Tigeot * Optional properties 1215718399fSFrançois Tigeot */ 12206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = 1235718399fSFrançois Tigeot { 1245718399fSFrançois Tigeot { DRM_MODE_SCALE_NONE, "None" }, 1255718399fSFrançois Tigeot { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 1265718399fSFrançois Tigeot { DRM_MODE_SCALE_CENTER, "Center" }, 1275718399fSFrançois Tigeot { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 1285718399fSFrançois Tigeot }; 1295718399fSFrançois Tigeot 1305718399fSFrançois Tigeot /* 1315718399fSFrançois Tigeot * Non-global properties, but "required" for certain connectors. 1325718399fSFrançois Tigeot */ 13306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = 1345718399fSFrançois Tigeot { 1355718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 1365718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 1375718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 1385718399fSFrançois Tigeot }; 1395718399fSFrançois Tigeot 1405718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 1415718399fSFrançois Tigeot 14206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = 1435718399fSFrançois Tigeot { 1445718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 1455718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 1465718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 1475718399fSFrançois Tigeot }; 1485718399fSFrançois Tigeot 1495718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 1505718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list) 1515718399fSFrançois Tigeot 15206fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] = 1535718399fSFrançois Tigeot { 1545718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 1555718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 1565718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 1575718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 1585718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 1595718399fSFrançois Tigeot }; 1605718399fSFrançois Tigeot 1615718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 1625718399fSFrançois Tigeot 16306fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = 1645718399fSFrançois Tigeot { 1655718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 1665718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 1675718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 1685718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 1695718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 1705718399fSFrançois Tigeot }; 1715718399fSFrançois Tigeot 1725718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 1735718399fSFrançois Tigeot drm_tv_subconnector_enum_list) 1745718399fSFrançois Tigeot 17506fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { 1765718399fSFrançois Tigeot { DRM_MODE_DIRTY_OFF, "Off" }, 1775718399fSFrançois Tigeot { DRM_MODE_DIRTY_ON, "On" }, 1785718399fSFrançois Tigeot { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, 1795718399fSFrançois Tigeot }; 1805718399fSFrançois Tigeot 1815718399fSFrançois Tigeot struct drm_conn_prop_enum_list { 1825718399fSFrançois Tigeot int type; 18306fede5aSFrançois Tigeot const char *name; 1845718399fSFrançois Tigeot int count; 1855718399fSFrançois Tigeot }; 1865718399fSFrançois Tigeot 1875718399fSFrançois Tigeot /* 1885718399fSFrançois Tigeot * Connector and encoder types. 1895718399fSFrançois Tigeot */ 1905718399fSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] = 191*9edbd4a0SFrançois Tigeot { { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, 192*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VGA, "VGA" }, 193*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 194*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 195*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 196*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Composite, "Composite" }, 197*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, 198*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 199*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Component, "Component" }, 200*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, 201*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, 202*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 203*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 204*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_TV, "TV" }, 205*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_eDP, "eDP" }, 206*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, 207*9edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DSI, "DSI" }, 2085718399fSFrançois Tigeot }; 2095718399fSFrançois Tigeot 21006fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] = 2115718399fSFrançois Tigeot { { DRM_MODE_ENCODER_NONE, "None" }, 2125718399fSFrançois Tigeot { DRM_MODE_ENCODER_DAC, "DAC" }, 2135718399fSFrançois Tigeot { DRM_MODE_ENCODER_TMDS, "TMDS" }, 2145718399fSFrançois Tigeot { DRM_MODE_ENCODER_LVDS, "LVDS" }, 2155718399fSFrançois Tigeot { DRM_MODE_ENCODER_TVDAC, "TV" }, 216b5162e19SFrançois Tigeot { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, 217*9edbd4a0SFrançois Tigeot { DRM_MODE_ENCODER_DSI, "DSI" }, 2185718399fSFrançois Tigeot }; 2195718399fSFrançois Tigeot 22006fede5aSFrançois Tigeot const char *drm_get_encoder_name(const struct drm_encoder *encoder) 2215718399fSFrançois Tigeot { 2225718399fSFrançois Tigeot static char buf[32]; 2235718399fSFrançois Tigeot 2245718399fSFrançois Tigeot ksnprintf(buf, 32, "%s-%d", 2255718399fSFrançois Tigeot drm_encoder_enum_list[encoder->encoder_type].name, 2265718399fSFrançois Tigeot encoder->base.id); 2275718399fSFrançois Tigeot return buf; 2285718399fSFrançois Tigeot } 229b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_get_encoder_name); 2305718399fSFrançois Tigeot 23106fede5aSFrançois Tigeot const char *drm_get_connector_name(const struct drm_connector *connector) 2325718399fSFrançois Tigeot { 2335718399fSFrançois Tigeot static char buf[32]; 2345718399fSFrançois Tigeot 2355718399fSFrançois Tigeot ksnprintf(buf, 32, "%s-%d", 2365718399fSFrançois Tigeot drm_connector_enum_list[connector->connector_type].name, 2375718399fSFrançois Tigeot connector->connector_type_id); 2385718399fSFrançois Tigeot return buf; 2395718399fSFrançois Tigeot } 240b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_name); 2415718399fSFrançois Tigeot 24206fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status) 2435718399fSFrançois Tigeot { 2445718399fSFrançois Tigeot if (status == connector_status_connected) 2455718399fSFrançois Tigeot return "connected"; 2465718399fSFrançois Tigeot else if (status == connector_status_disconnected) 2475718399fSFrançois Tigeot return "disconnected"; 2485718399fSFrançois Tigeot else 2495718399fSFrançois Tigeot return "unknown"; 2505718399fSFrançois Tigeot } 2514dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name); 2525718399fSFrançois Tigeot 25306fede5aSFrançois Tigeot static char printable_char(int c) 25406fede5aSFrançois Tigeot { 25506fede5aSFrançois Tigeot return isascii(c) && isprint(c) ? c : '?'; 25606fede5aSFrançois Tigeot } 25706fede5aSFrançois Tigeot 25806fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format) 25906fede5aSFrançois Tigeot { 26006fede5aSFrançois Tigeot static char buf[32]; 26106fede5aSFrançois Tigeot 26206fede5aSFrançois Tigeot ksnprintf(buf, sizeof(buf), 26306fede5aSFrançois Tigeot "%c%c%c%c %s-endian (0x%08x)", 26406fede5aSFrançois Tigeot printable_char(format & 0xff), 26506fede5aSFrançois Tigeot printable_char((format >> 8) & 0xff), 26606fede5aSFrançois Tigeot printable_char((format >> 16) & 0xff), 26706fede5aSFrançois Tigeot printable_char((format >> 24) & 0x7f), 26806fede5aSFrançois Tigeot format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", 26906fede5aSFrançois Tigeot format); 27006fede5aSFrançois Tigeot 27106fede5aSFrançois Tigeot return buf; 27206fede5aSFrançois Tigeot } 27306fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name); 27406fede5aSFrançois Tigeot 2755718399fSFrançois Tigeot /** 2764dbb207bSFrançois Tigeot * drm_mode_object_get - allocate a new modeset identifier 2775718399fSFrançois Tigeot * @dev: DRM device 2784dbb207bSFrançois Tigeot * @obj: object pointer, used to generate unique ID 2794dbb207bSFrançois Tigeot * @obj_type: object type 2805718399fSFrançois Tigeot * 2815718399fSFrançois Tigeot * Create a unique identifier based on @ptr in @dev's identifier space. Used 2825718399fSFrançois Tigeot * for tracking modes, CRTCs and connectors. 2835718399fSFrançois Tigeot * 2845718399fSFrançois Tigeot * RETURNS: 2855718399fSFrançois Tigeot * New unique (relative to other objects in @dev) integer identifier for the 2865718399fSFrançois Tigeot * object. 2875718399fSFrançois Tigeot */ 2885718399fSFrançois Tigeot static int drm_mode_object_get(struct drm_device *dev, 2895718399fSFrançois Tigeot struct drm_mode_object *obj, uint32_t obj_type) 2905718399fSFrançois Tigeot { 2915718399fSFrançois Tigeot int ret; 2925718399fSFrançois Tigeot 2939f0f5970SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 2949f0f5970SFrançois Tigeot ret = idr_alloc(&dev->mode_config.crtc_idr, obj, 1, 0, GFP_KERNEL); 2959f0f5970SFrançois Tigeot if (ret >= 0) { 2964dbb207bSFrançois Tigeot /* 2974dbb207bSFrançois Tigeot * Set up the object linking under the protection of the idr 2984dbb207bSFrançois Tigeot * lock so that other users can't see inconsistent state. 2994dbb207bSFrançois Tigeot */ 3009f0f5970SFrançois Tigeot obj->id = ret; 3015718399fSFrançois Tigeot obj->type = obj_type; 3029f0f5970SFrançois Tigeot } 3039f0f5970SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3049f0f5970SFrançois Tigeot 3059f0f5970SFrançois Tigeot return ret < 0 ? ret : 0; 3065718399fSFrançois Tigeot } 3075718399fSFrançois Tigeot 3085718399fSFrançois Tigeot /** 3094dbb207bSFrançois Tigeot * drm_mode_object_put - free a modeset identifer 3105718399fSFrançois Tigeot * @dev: DRM device 3114dbb207bSFrançois Tigeot * @object: object to free 3125718399fSFrançois Tigeot * 3135718399fSFrançois Tigeot * Free @id from @dev's unique identifier pool. 3145718399fSFrançois Tigeot */ 3155718399fSFrançois Tigeot static void drm_mode_object_put(struct drm_device *dev, 3165718399fSFrançois Tigeot struct drm_mode_object *object) 3175718399fSFrançois Tigeot { 3184dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 319aea8bdbdSFrançois Tigeot idr_remove(&dev->mode_config.crtc_idr, object->id); 3204dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3215718399fSFrançois Tigeot } 3225718399fSFrançois Tigeot 3234dbb207bSFrançois Tigeot /** 3244dbb207bSFrançois Tigeot * drm_mode_object_find - look up a drm object with static lifetime 3254dbb207bSFrançois Tigeot * @dev: drm device 3264dbb207bSFrançois Tigeot * @id: id of the mode object 3274dbb207bSFrançois Tigeot * @type: type of the mode object 3284dbb207bSFrançois Tigeot * 3294dbb207bSFrançois Tigeot * Note that framebuffers cannot be looked up with this functions - since those 3304dbb207bSFrançois Tigeot * are reference counted, they need special treatment. 3314dbb207bSFrançois Tigeot */ 3325718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, 3335718399fSFrançois Tigeot uint32_t id, uint32_t type) 3345718399fSFrançois Tigeot { 335aea8bdbdSFrançois Tigeot struct drm_mode_object *obj = NULL; 3365718399fSFrançois Tigeot 3374dbb207bSFrançois Tigeot /* Framebuffers are reference counted and need their own lookup 3384dbb207bSFrançois Tigeot * function.*/ 3394dbb207bSFrançois Tigeot WARN_ON(type == DRM_MODE_OBJECT_FB); 3404dbb207bSFrançois Tigeot 3414dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 342aea8bdbdSFrançois Tigeot obj = idr_find(&dev->mode_config.crtc_idr, id); 3435718399fSFrançois Tigeot if (!obj || (obj->type != type) || (obj->id != id)) 3445718399fSFrançois Tigeot obj = NULL; 3454dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3465718399fSFrançois Tigeot 3475718399fSFrançois Tigeot return obj; 3485718399fSFrançois Tigeot } 349b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find); 3505718399fSFrançois Tigeot 3515718399fSFrançois Tigeot /** 3525718399fSFrançois Tigeot * drm_framebuffer_init - initialize a framebuffer 3535718399fSFrançois Tigeot * @dev: DRM device 3544dbb207bSFrançois Tigeot * @fb: framebuffer to be initialized 3554dbb207bSFrançois Tigeot * @funcs: ... with these functions 3565718399fSFrançois Tigeot * 3575718399fSFrançois Tigeot * Allocates an ID for the framebuffer's parent mode object, sets its mode 3585718399fSFrançois Tigeot * functions & device file and adds it to the master fd list. 3595718399fSFrançois Tigeot * 3604dbb207bSFrançois Tigeot * IMPORTANT: 3614dbb207bSFrançois Tigeot * This functions publishes the fb and makes it available for concurrent access 3624dbb207bSFrançois Tigeot * by other users. Which means by this point the fb _must_ be fully set up - 3634dbb207bSFrançois Tigeot * since all the fb attributes are invariant over its lifetime, no further 3644dbb207bSFrançois Tigeot * locking but only correct reference counting is required. 3654dbb207bSFrançois Tigeot * 3665718399fSFrançois Tigeot * RETURNS: 3675718399fSFrançois Tigeot * Zero on success, error code on failure. 3685718399fSFrançois Tigeot */ 3695718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 3705718399fSFrançois Tigeot const struct drm_framebuffer_funcs *funcs) 3715718399fSFrançois Tigeot { 3725718399fSFrançois Tigeot int ret; 3735718399fSFrançois Tigeot 3744dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 375b5162e19SFrançois Tigeot kref_init(&fb->refcount); 3764dbb207bSFrançois Tigeot INIT_LIST_HEAD(&fb->filp_head); 3774dbb207bSFrançois Tigeot fb->dev = dev; 3784dbb207bSFrançois Tigeot fb->funcs = funcs; 379b5162e19SFrançois Tigeot 3805718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &fb->base, DRM_MODE_OBJECT_FB); 3815718399fSFrançois Tigeot if (ret) 3824dbb207bSFrançois Tigeot goto out; 3835718399fSFrançois Tigeot 3844dbb207bSFrançois Tigeot /* Grab the idr reference. */ 3854dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 3864dbb207bSFrançois Tigeot 3875718399fSFrançois Tigeot dev->mode_config.num_fb++; 3885718399fSFrançois Tigeot list_add(&fb->head, &dev->mode_config.fb_list); 3894dbb207bSFrançois Tigeot out: 3904dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 3915718399fSFrançois Tigeot 3925718399fSFrançois Tigeot return 0; 3935718399fSFrançois Tigeot } 394b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init); 395b5162e19SFrançois Tigeot 396b5162e19SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref) 397b5162e19SFrançois Tigeot { 398b5162e19SFrançois Tigeot struct drm_framebuffer *fb = 399b5162e19SFrançois Tigeot container_of(kref, struct drm_framebuffer, refcount); 400b5162e19SFrançois Tigeot fb->funcs->destroy(fb); 401b5162e19SFrançois Tigeot } 402b5162e19SFrançois Tigeot 4034dbb207bSFrançois Tigeot static struct drm_framebuffer *__drm_framebuffer_lookup(struct drm_device *dev, 4044dbb207bSFrançois Tigeot uint32_t id) 4054dbb207bSFrançois Tigeot { 4064dbb207bSFrançois Tigeot struct drm_mode_object *obj = NULL; 4074dbb207bSFrançois Tigeot struct drm_framebuffer *fb; 4084dbb207bSFrançois Tigeot 4094dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 4104dbb207bSFrançois Tigeot obj = idr_find(&dev->mode_config.crtc_idr, id); 4114dbb207bSFrançois Tigeot if (!obj || (obj->type != DRM_MODE_OBJECT_FB) || (obj->id != id)) 4124dbb207bSFrançois Tigeot fb = NULL; 4134dbb207bSFrançois Tigeot else 4144dbb207bSFrançois Tigeot fb = obj_to_fb(obj); 4154dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 4164dbb207bSFrançois Tigeot 4174dbb207bSFrançois Tigeot return fb; 4184dbb207bSFrançois Tigeot } 4194dbb207bSFrançois Tigeot 4204dbb207bSFrançois Tigeot /** 4214dbb207bSFrançois Tigeot * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference 4224dbb207bSFrançois Tigeot * @dev: drm device 4234dbb207bSFrançois Tigeot * @id: id of the fb object 4244dbb207bSFrançois Tigeot * 4254dbb207bSFrançois Tigeot * If successful, this grabs an additional reference to the framebuffer - 4264dbb207bSFrançois Tigeot * callers need to make sure to eventually unreference the returned framebuffer 4274dbb207bSFrançois Tigeot * again. 4284dbb207bSFrançois Tigeot */ 4294dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, 4304dbb207bSFrançois Tigeot uint32_t id) 4314dbb207bSFrançois Tigeot { 4324dbb207bSFrançois Tigeot struct drm_framebuffer *fb; 4334dbb207bSFrançois Tigeot 4344dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 4354dbb207bSFrançois Tigeot fb = __drm_framebuffer_lookup(dev, id); 4364dbb207bSFrançois Tigeot if (fb) 4374dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 4384dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 4394dbb207bSFrançois Tigeot 4404dbb207bSFrançois Tigeot return fb; 4414dbb207bSFrançois Tigeot } 4424dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup); 4434dbb207bSFrançois Tigeot 444b5162e19SFrançois Tigeot /** 445b5162e19SFrançois Tigeot * drm_framebuffer_unreference - unref a framebuffer 4464dbb207bSFrançois Tigeot * @fb: framebuffer to unref 447b5162e19SFrançois Tigeot * 4484dbb207bSFrançois Tigeot * This functions decrements the fb's refcount and frees it if it drops to zero. 449b5162e19SFrançois Tigeot */ 450b5162e19SFrançois Tigeot void drm_framebuffer_unreference(struct drm_framebuffer *fb) 451b5162e19SFrançois Tigeot { 452b5162e19SFrançois Tigeot DRM_DEBUG("FB ID: %d\n", fb->base.id); 453b5162e19SFrançois Tigeot kref_put(&fb->refcount, drm_framebuffer_free); 454b5162e19SFrançois Tigeot } 455b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unreference); 456b5162e19SFrançois Tigeot 457b5162e19SFrançois Tigeot /** 458b5162e19SFrançois Tigeot * drm_framebuffer_reference - incr the fb refcnt 4594dbb207bSFrançois Tigeot * @fb: framebuffer 460b5162e19SFrançois Tigeot */ 461b5162e19SFrançois Tigeot void drm_framebuffer_reference(struct drm_framebuffer *fb) 462b5162e19SFrançois Tigeot { 463b5162e19SFrançois Tigeot DRM_DEBUG("FB ID: %d\n", fb->base.id); 464b5162e19SFrançois Tigeot kref_get(&fb->refcount); 465b5162e19SFrançois Tigeot } 466b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_reference); 4675718399fSFrançois Tigeot 468a2fdbec6SFrançois Tigeot static void drm_framebuffer_free_bug(struct kref *kref) 469a2fdbec6SFrançois Tigeot { 470a2fdbec6SFrançois Tigeot BUG(); 471a2fdbec6SFrançois Tigeot } 472a2fdbec6SFrançois Tigeot 473a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unreference(struct drm_framebuffer *fb) 474a2fdbec6SFrançois Tigeot { 475a2fdbec6SFrançois Tigeot DRM_DEBUG("FB ID: %d\n", fb->base.id); 476a2fdbec6SFrançois Tigeot kref_put(&fb->refcount, drm_framebuffer_free_bug); 477a2fdbec6SFrançois Tigeot } 478a2fdbec6SFrançois Tigeot 479a2fdbec6SFrançois Tigeot /* dev->mode_config.fb_lock must be held! */ 480a2fdbec6SFrançois Tigeot static void __drm_framebuffer_unregister(struct drm_device *dev, 481a2fdbec6SFrançois Tigeot struct drm_framebuffer *fb) 482a2fdbec6SFrançois Tigeot { 483a2fdbec6SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 484a2fdbec6SFrançois Tigeot idr_remove(&dev->mode_config.crtc_idr, fb->base.id); 485a2fdbec6SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 486a2fdbec6SFrançois Tigeot 487a2fdbec6SFrançois Tigeot fb->base.id = 0; 488a2fdbec6SFrançois Tigeot 489a2fdbec6SFrançois Tigeot __drm_framebuffer_unreference(fb); 490a2fdbec6SFrançois Tigeot } 491a2fdbec6SFrançois Tigeot 492a2fdbec6SFrançois Tigeot /** 493a2fdbec6SFrançois Tigeot * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 494a2fdbec6SFrançois Tigeot * @fb: fb to unregister 495a2fdbec6SFrançois Tigeot * 496a2fdbec6SFrançois Tigeot * Drivers need to call this when cleaning up driver-private framebuffers, e.g. 497a2fdbec6SFrançois Tigeot * those used for fbdev. Note that the caller must hold a reference of it's own, 498a2fdbec6SFrançois Tigeot * i.e. the object may not be destroyed through this call (since it'll lead to a 499a2fdbec6SFrançois Tigeot * locking inversion). 500a2fdbec6SFrançois Tigeot */ 501a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) 502a2fdbec6SFrançois Tigeot { 503a2fdbec6SFrançois Tigeot struct drm_device *dev = fb->dev; 504a2fdbec6SFrançois Tigeot 505a2fdbec6SFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 506a2fdbec6SFrançois Tigeot /* Mark fb as reaped and drop idr ref. */ 507a2fdbec6SFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 508a2fdbec6SFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 509a2fdbec6SFrançois Tigeot } 510a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private); 511a2fdbec6SFrançois Tigeot 5125718399fSFrançois Tigeot /** 5135718399fSFrançois Tigeot * drm_framebuffer_cleanup - remove a framebuffer object 5145718399fSFrançois Tigeot * @fb: framebuffer to remove 5155718399fSFrançois Tigeot * 5164dbb207bSFrançois Tigeot * Cleanup references to a user-created framebuffer. This function is intended 5174dbb207bSFrançois Tigeot * to be used from the drivers ->destroy callback. 5185718399fSFrançois Tigeot * 5194dbb207bSFrançois Tigeot * Note that this function does not remove the fb from active usuage - if it is 5204dbb207bSFrançois Tigeot * still used anywhere, hilarity can ensue since userspace could call getfb on 5214dbb207bSFrançois Tigeot * the id and get back -EINVAL. Obviously no concern at driver unload time. 5224dbb207bSFrançois Tigeot * 5234dbb207bSFrançois Tigeot * Also, the framebuffer will not be removed from the lookup idr - for 5244dbb207bSFrançois Tigeot * user-created framebuffers this will happen in in the rmfb ioctl. For 5254dbb207bSFrançois Tigeot * driver-private objects (e.g. for fbdev) drivers need to explicitly call 5264dbb207bSFrançois Tigeot * drm_framebuffer_unregister_private. 5275718399fSFrançois Tigeot */ 5285718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 5295718399fSFrançois Tigeot { 5305718399fSFrançois Tigeot struct drm_device *dev = fb->dev; 5314dbb207bSFrançois Tigeot 5324dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 533b5162e19SFrançois Tigeot list_del(&fb->head); 534b5162e19SFrançois Tigeot dev->mode_config.num_fb--; 5354dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 536b5162e19SFrançois Tigeot } 537b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup); 538b5162e19SFrançois Tigeot 539b5162e19SFrançois Tigeot /** 540b5162e19SFrançois Tigeot * drm_framebuffer_remove - remove and unreference a framebuffer object 541b5162e19SFrançois Tigeot * @fb: framebuffer to remove 542b5162e19SFrançois Tigeot * 543b5162e19SFrançois Tigeot * Scans all the CRTCs and planes in @dev's mode_config. If they're 5444dbb207bSFrançois Tigeot * using @fb, removes it, setting it to NULL. Then drops the reference to the 5454dbb207bSFrançois Tigeot * passed-in framebuffer. Might take the modeset locks. 5464dbb207bSFrançois Tigeot * 5474dbb207bSFrançois Tigeot * Note that this function optimizes the cleanup away if the caller holds the 5484dbb207bSFrançois Tigeot * last reference to the framebuffer. It is also guaranteed to not take the 5494dbb207bSFrançois Tigeot * modeset locks in this case. 550b5162e19SFrançois Tigeot */ 551b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb) 552b5162e19SFrançois Tigeot { 553b5162e19SFrançois Tigeot struct drm_device *dev = fb->dev; 5545718399fSFrançois Tigeot struct drm_crtc *crtc; 5555718399fSFrançois Tigeot struct drm_plane *plane; 5565718399fSFrançois Tigeot struct drm_mode_set set; 5575718399fSFrançois Tigeot int ret; 5585718399fSFrançois Tigeot 5594dbb207bSFrançois Tigeot WARN_ON(!list_empty(&fb->filp_head)); 5604dbb207bSFrançois Tigeot 5614dbb207bSFrançois Tigeot /* 5624dbb207bSFrançois Tigeot * drm ABI mandates that we remove any deleted framebuffers from active 5634dbb207bSFrançois Tigeot * useage. But since most sane clients only remove framebuffers they no 5644dbb207bSFrançois Tigeot * longer need, try to optimize this away. 5654dbb207bSFrançois Tigeot * 5664dbb207bSFrançois Tigeot * Since we're holding a reference ourselves, observing a refcount of 1 5674dbb207bSFrançois Tigeot * means that we're the last holder and can skip it. Also, the refcount 5684dbb207bSFrançois Tigeot * can never increase from 1 again, so we don't need any barriers or 5694dbb207bSFrançois Tigeot * locks. 5704dbb207bSFrançois Tigeot * 5714dbb207bSFrançois Tigeot * Note that userspace could try to race with use and instate a new 5724dbb207bSFrançois Tigeot * usage _after_ we've cleared all current ones. End result will be an 5734dbb207bSFrançois Tigeot * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot 5744dbb207bSFrançois Tigeot * in this manner. 5754dbb207bSFrançois Tigeot */ 5764dbb207bSFrançois Tigeot if (atomic_read(&fb->refcount.refcount) > 1) { 5774dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 5785718399fSFrançois Tigeot /* remove from any CRTC */ 5795718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) { 5805718399fSFrançois Tigeot if (crtc->fb == fb) { 5815718399fSFrançois Tigeot /* should turn off the crtc */ 5825718399fSFrançois Tigeot memset(&set, 0, sizeof(struct drm_mode_set)); 5835718399fSFrançois Tigeot set.crtc = crtc; 5845718399fSFrançois Tigeot set.fb = NULL; 5854dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 5865718399fSFrançois Tigeot if (ret) 5875718399fSFrançois Tigeot DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 5885718399fSFrançois Tigeot } 5895718399fSFrançois Tigeot } 5905718399fSFrançois Tigeot 5915718399fSFrançois Tigeot list_for_each_entry(plane, &dev->mode_config.plane_list, head) { 59206fede5aSFrançois Tigeot if (plane->fb == fb) 59306fede5aSFrançois Tigeot drm_plane_force_disable(plane); 5945718399fSFrançois Tigeot } 5954dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 5964dbb207bSFrançois Tigeot } 597b5162e19SFrançois Tigeot 598b5162e19SFrançois Tigeot drm_framebuffer_unreference(fb); 5995718399fSFrançois Tigeot } 600b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove); 6015718399fSFrançois Tigeot 6025718399fSFrançois Tigeot /** 6035718399fSFrançois Tigeot * drm_crtc_init - Initialise a new CRTC object 6045718399fSFrançois Tigeot * @dev: DRM device 6055718399fSFrançois Tigeot * @crtc: CRTC object to init 6065718399fSFrançois Tigeot * @funcs: callbacks for the new CRTC 6075718399fSFrançois Tigeot * 60806fede5aSFrançois Tigeot * Inits a new object created as base part of a driver crtc object. 6095718399fSFrançois Tigeot * 6105718399fSFrançois Tigeot * RETURNS: 6115718399fSFrançois Tigeot * Zero on success, error code on failure. 6125718399fSFrançois Tigeot */ 6135718399fSFrançois Tigeot int drm_crtc_init(struct drm_device *dev, struct drm_crtc *crtc, 6145718399fSFrançois Tigeot const struct drm_crtc_funcs *funcs) 6155718399fSFrançois Tigeot { 6165718399fSFrançois Tigeot int ret; 6175718399fSFrançois Tigeot 6185718399fSFrançois Tigeot crtc->dev = dev; 6195718399fSFrançois Tigeot crtc->funcs = funcs; 620b5162e19SFrançois Tigeot crtc->invert_dimensions = false; 6215718399fSFrançois Tigeot 6224dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 6234dbb207bSFrançois Tigeot lockinit(&crtc->mutex, "drmcm", 0, LK_CANRECURSE); 6244dbb207bSFrançois Tigeot lockmgr(&crtc->mutex, LK_EXCLUSIVE); 625b5162e19SFrançois Tigeot 6265718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 6275718399fSFrançois Tigeot if (ret) 6285718399fSFrançois Tigeot goto out; 6295718399fSFrançois Tigeot 630b5162e19SFrançois Tigeot crtc->base.properties = &crtc->properties; 631b5162e19SFrançois Tigeot 6325718399fSFrançois Tigeot list_add_tail(&crtc->head, &dev->mode_config.crtc_list); 6335718399fSFrançois Tigeot dev->mode_config.num_crtc++; 634b5162e19SFrançois Tigeot 6355718399fSFrançois Tigeot out: 6364dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 6375718399fSFrançois Tigeot 6385718399fSFrançois Tigeot return ret; 6395718399fSFrançois Tigeot } 640b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init); 6415718399fSFrançois Tigeot 6425718399fSFrançois Tigeot /** 64306fede5aSFrançois Tigeot * drm_crtc_cleanup - Clean up the core crtc usage 6445718399fSFrançois Tigeot * @crtc: CRTC to cleanup 6455718399fSFrançois Tigeot * 64606fede5aSFrançois Tigeot * This function cleans up @crtc and removes it from the DRM mode setting 64706fede5aSFrançois Tigeot * core. Note that the function does *not* free the crtc structure itself, 64806fede5aSFrançois Tigeot * this is the responsibility of the caller. 6495718399fSFrançois Tigeot */ 6505718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc) 6515718399fSFrançois Tigeot { 6525718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 6535718399fSFrançois Tigeot 6544dbb207bSFrançois Tigeot kfree(crtc->gamma_store); 6555718399fSFrançois Tigeot crtc->gamma_store = NULL; 6565718399fSFrançois Tigeot 6575718399fSFrançois Tigeot drm_mode_object_put(dev, &crtc->base); 6585718399fSFrançois Tigeot list_del(&crtc->head); 6595718399fSFrançois Tigeot dev->mode_config.num_crtc--; 6605718399fSFrançois Tigeot } 661b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup); 6625718399fSFrançois Tigeot 6635718399fSFrançois Tigeot /** 664*9edbd4a0SFrançois Tigeot * drm_crtc_index - find the index of a registered CRTC 665*9edbd4a0SFrançois Tigeot * @crtc: CRTC to find index for 666*9edbd4a0SFrançois Tigeot * 667*9edbd4a0SFrançois Tigeot * Given a registered CRTC, return the index of that CRTC within a DRM 668*9edbd4a0SFrançois Tigeot * device's list of CRTCs. 669*9edbd4a0SFrançois Tigeot */ 670*9edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc) 671*9edbd4a0SFrançois Tigeot { 672*9edbd4a0SFrançois Tigeot unsigned int index = 0; 673*9edbd4a0SFrançois Tigeot struct drm_crtc *tmp; 674*9edbd4a0SFrançois Tigeot 675*9edbd4a0SFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { 676*9edbd4a0SFrançois Tigeot if (tmp == crtc) 677*9edbd4a0SFrançois Tigeot return index; 678*9edbd4a0SFrançois Tigeot 679*9edbd4a0SFrançois Tigeot index++; 680*9edbd4a0SFrançois Tigeot } 681*9edbd4a0SFrançois Tigeot 682*9edbd4a0SFrançois Tigeot BUG(); 683*9edbd4a0SFrançois Tigeot } 684*9edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index); 685*9edbd4a0SFrançois Tigeot 686*9edbd4a0SFrançois Tigeot /** 6875718399fSFrançois Tigeot * drm_mode_probed_add - add a mode to a connector's probed mode list 6885718399fSFrançois Tigeot * @connector: connector the new mode 6895718399fSFrançois Tigeot * @mode: mode data 6905718399fSFrançois Tigeot * 6915718399fSFrançois Tigeot * Add @mode to @connector's mode list for later use. 6925718399fSFrançois Tigeot */ 6935718399fSFrançois Tigeot void drm_mode_probed_add(struct drm_connector *connector, 6945718399fSFrançois Tigeot struct drm_display_mode *mode) 6955718399fSFrançois Tigeot { 69606fede5aSFrançois Tigeot list_add_tail(&mode->head, &connector->probed_modes); 6975718399fSFrançois Tigeot } 698b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_probed_add); 6995718399fSFrançois Tigeot 700*9edbd4a0SFrançois Tigeot /* 7015718399fSFrançois Tigeot * drm_mode_remove - remove and free a mode 7025718399fSFrançois Tigeot * @connector: connector list to modify 7035718399fSFrançois Tigeot * @mode: mode to remove 7045718399fSFrançois Tigeot * 7055718399fSFrançois Tigeot * Remove @mode from @connector's mode list, then free it. 7065718399fSFrançois Tigeot */ 707*9edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector, 7085718399fSFrançois Tigeot struct drm_display_mode *mode) 7095718399fSFrançois Tigeot { 7105718399fSFrançois Tigeot list_del(&mode->head); 7115718399fSFrançois Tigeot drm_mode_destroy(connector->dev, mode); 7125718399fSFrançois Tigeot } 7135718399fSFrançois Tigeot 7145718399fSFrançois Tigeot /** 7155718399fSFrançois Tigeot * drm_connector_init - Init a preallocated connector 7165718399fSFrançois Tigeot * @dev: DRM device 7175718399fSFrançois Tigeot * @connector: the connector to init 7185718399fSFrançois Tigeot * @funcs: callbacks for this connector 719a2fdbec6SFrançois Tigeot * @connector_type: user visible type of the connector 7205718399fSFrançois Tigeot * 7215718399fSFrançois Tigeot * Initialises a preallocated connector. Connectors should be 7225718399fSFrançois Tigeot * subclassed as part of driver connector objects. 7235718399fSFrançois Tigeot * 7245718399fSFrançois Tigeot * RETURNS: 7255718399fSFrançois Tigeot * Zero on success, error code on failure. 7265718399fSFrançois Tigeot */ 7275718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev, 7285718399fSFrançois Tigeot struct drm_connector *connector, 7295718399fSFrançois Tigeot const struct drm_connector_funcs *funcs, 7305718399fSFrançois Tigeot int connector_type) 7315718399fSFrançois Tigeot { 7325718399fSFrançois Tigeot int ret; 7335718399fSFrançois Tigeot 7344dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 7355718399fSFrançois Tigeot 7365718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &connector->base, DRM_MODE_OBJECT_CONNECTOR); 7375718399fSFrançois Tigeot if (ret) 7385718399fSFrançois Tigeot goto out; 7395718399fSFrançois Tigeot 740b5162e19SFrançois Tigeot connector->base.properties = &connector->properties; 7415718399fSFrançois Tigeot connector->dev = dev; 7425718399fSFrançois Tigeot connector->funcs = funcs; 7435718399fSFrançois Tigeot connector->connector_type = connector_type; 7445718399fSFrançois Tigeot connector->connector_type_id = 7455718399fSFrançois Tigeot ++drm_connector_enum_list[connector_type].count; /* TODO */ 746*9edbd4a0SFrançois Tigeot if (connector->connector_type_id < 0) { 747*9edbd4a0SFrançois Tigeot ret = connector->connector_type_id; 748*9edbd4a0SFrançois Tigeot drm_mode_object_put(dev, &connector->base); 749*9edbd4a0SFrançois Tigeot goto out; 750*9edbd4a0SFrançois Tigeot } 7515718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->probed_modes); 7525718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->modes); 7535718399fSFrançois Tigeot connector->edid_blob_ptr = NULL; 754b5162e19SFrançois Tigeot connector->status = connector_status_unknown; 7555718399fSFrançois Tigeot 7565718399fSFrançois Tigeot list_add_tail(&connector->head, &dev->mode_config.connector_list); 7575718399fSFrançois Tigeot dev->mode_config.num_connector++; 7585718399fSFrançois Tigeot 759b5162e19SFrançois Tigeot if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) 760b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 761b5162e19SFrançois Tigeot dev->mode_config.edid_property, 762b5162e19SFrançois Tigeot 0); 7635718399fSFrançois Tigeot 764b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 7655718399fSFrançois Tigeot dev->mode_config.dpms_property, 0); 7665718399fSFrançois Tigeot 7675718399fSFrançois Tigeot out: 7684dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 7695718399fSFrançois Tigeot 7705718399fSFrançois Tigeot return ret; 7715718399fSFrançois Tigeot } 772b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init); 7735718399fSFrançois Tigeot 7745718399fSFrançois Tigeot /** 7755718399fSFrançois Tigeot * drm_connector_cleanup - cleans up an initialised connector 7765718399fSFrançois Tigeot * @connector: connector to cleanup 7775718399fSFrançois Tigeot * 7785718399fSFrançois Tigeot * Cleans up the connector but doesn't free the object. 7795718399fSFrançois Tigeot */ 7805718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector) 7815718399fSFrançois Tigeot { 7825718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 7835718399fSFrançois Tigeot struct drm_display_mode *mode, *t; 7845718399fSFrançois Tigeot 7855718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 7865718399fSFrançois Tigeot drm_mode_remove(connector, mode); 7875718399fSFrançois Tigeot 7885718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->modes, head) 7895718399fSFrançois Tigeot drm_mode_remove(connector, mode); 7905718399fSFrançois Tigeot 7915718399fSFrançois Tigeot drm_mode_object_put(dev, &connector->base); 7925718399fSFrançois Tigeot list_del(&connector->head); 7935718399fSFrançois Tigeot dev->mode_config.num_connector--; 7945718399fSFrançois Tigeot } 795b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup); 796b5162e19SFrançois Tigeot 797b5162e19SFrançois Tigeot void drm_connector_unplug_all(struct drm_device *dev) 798b5162e19SFrançois Tigeot { 799b5162e19SFrançois Tigeot #if 0 800b5162e19SFrançois Tigeot struct drm_connector *connector; 801b5162e19SFrançois Tigeot 802b5162e19SFrançois Tigeot /* taking the mode config mutex ends up in a clash with sysfs */ 803b5162e19SFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 804b5162e19SFrançois Tigeot drm_sysfs_connector_remove(connector); 8054dbb207bSFrançois Tigeot 806*9edbd4a0SFrançois Tigeot #endif 807b5162e19SFrançois Tigeot } 808b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unplug_all); 8095718399fSFrançois Tigeot 8105718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev, 8115718399fSFrançois Tigeot struct drm_encoder *encoder, 8125718399fSFrançois Tigeot const struct drm_encoder_funcs *funcs, 8135718399fSFrançois Tigeot int encoder_type) 8145718399fSFrançois Tigeot { 8155718399fSFrançois Tigeot int ret; 8165718399fSFrançois Tigeot 8174dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 8185718399fSFrançois Tigeot 8195718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); 8205718399fSFrançois Tigeot if (ret) 8215718399fSFrançois Tigeot goto out; 8225718399fSFrançois Tigeot 8235718399fSFrançois Tigeot encoder->dev = dev; 8245718399fSFrançois Tigeot encoder->encoder_type = encoder_type; 8255718399fSFrançois Tigeot encoder->funcs = funcs; 8265718399fSFrançois Tigeot 8275718399fSFrançois Tigeot list_add_tail(&encoder->head, &dev->mode_config.encoder_list); 8285718399fSFrançois Tigeot dev->mode_config.num_encoder++; 8295718399fSFrançois Tigeot 8305718399fSFrançois Tigeot out: 8314dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 8325718399fSFrançois Tigeot 8335718399fSFrançois Tigeot return ret; 8345718399fSFrançois Tigeot } 835b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init); 8365718399fSFrançois Tigeot 8375718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder) 8385718399fSFrançois Tigeot { 8395718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 8404dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 8415718399fSFrançois Tigeot drm_mode_object_put(dev, &encoder->base); 8425718399fSFrançois Tigeot list_del(&encoder->head); 8435718399fSFrançois Tigeot dev->mode_config.num_encoder--; 8444dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 8455718399fSFrançois Tigeot } 846b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup); 8475718399fSFrançois Tigeot 84806fede5aSFrançois Tigeot /** 84906fede5aSFrançois Tigeot * drm_plane_init - Initialise a new plane object 85006fede5aSFrançois Tigeot * @dev: DRM device 85106fede5aSFrançois Tigeot * @plane: plane object to init 85206fede5aSFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 85306fede5aSFrançois Tigeot * @funcs: callbacks for the new plane 85406fede5aSFrançois Tigeot * @formats: array of supported formats (%DRM_FORMAT_*) 85506fede5aSFrançois Tigeot * @format_count: number of elements in @formats 85606fede5aSFrançois Tigeot * @priv: plane is private (hidden from userspace)? 85706fede5aSFrançois Tigeot * 85806fede5aSFrançois Tigeot * Inits a new object created as base part of a driver plane object. 85906fede5aSFrançois Tigeot * 86006fede5aSFrançois Tigeot * RETURNS: 86106fede5aSFrançois Tigeot * Zero on success, error code on failure. 86206fede5aSFrançois Tigeot */ 8635718399fSFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 8645718399fSFrançois Tigeot unsigned long possible_crtcs, 8655718399fSFrançois Tigeot const struct drm_plane_funcs *funcs, 8665718399fSFrançois Tigeot const uint32_t *formats, uint32_t format_count, 8675718399fSFrançois Tigeot bool priv) 8685718399fSFrançois Tigeot { 8695718399fSFrançois Tigeot int ret; 8705718399fSFrançois Tigeot 8714dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 8725718399fSFrançois Tigeot 8735718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 8745718399fSFrançois Tigeot if (ret) 8755718399fSFrançois Tigeot goto out; 8765718399fSFrançois Tigeot 877b5162e19SFrançois Tigeot plane->base.properties = &plane->properties; 8785718399fSFrançois Tigeot plane->dev = dev; 8795718399fSFrançois Tigeot plane->funcs = funcs; 8805718399fSFrançois Tigeot plane->format_types = kmalloc(sizeof(uint32_t) * format_count, 8815a3b77d5SFrançois Tigeot M_DRM, M_WAITOK); 882b5162e19SFrançois Tigeot if (!plane->format_types) { 883b5162e19SFrançois Tigeot DRM_DEBUG_KMS("out of memory when allocating plane\n"); 884b5162e19SFrançois Tigeot drm_mode_object_put(dev, &plane->base); 885b5162e19SFrançois Tigeot ret = -ENOMEM; 886b5162e19SFrançois Tigeot goto out; 887b5162e19SFrançois Tigeot } 8885718399fSFrançois Tigeot 8895718399fSFrançois Tigeot memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 8905718399fSFrançois Tigeot plane->format_count = format_count; 8915718399fSFrançois Tigeot plane->possible_crtcs = possible_crtcs; 8925718399fSFrançois Tigeot 8935718399fSFrançois Tigeot /* private planes are not exposed to userspace, but depending on 8945718399fSFrançois Tigeot * display hardware, might be convenient to allow sharing programming 8955718399fSFrançois Tigeot * for the scanout engine with the crtc implementation. 8965718399fSFrançois Tigeot */ 8975718399fSFrançois Tigeot if (!priv) { 8985718399fSFrançois Tigeot list_add_tail(&plane->head, &dev->mode_config.plane_list); 8995718399fSFrançois Tigeot dev->mode_config.num_plane++; 9005718399fSFrançois Tigeot } else { 9015718399fSFrançois Tigeot INIT_LIST_HEAD(&plane->head); 9025718399fSFrançois Tigeot } 9035718399fSFrançois Tigeot 9045718399fSFrançois Tigeot out: 9054dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 9065718399fSFrançois Tigeot 9075718399fSFrançois Tigeot return ret; 9085718399fSFrançois Tigeot } 909b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init); 9105718399fSFrançois Tigeot 91106fede5aSFrançois Tigeot /** 91206fede5aSFrançois Tigeot * drm_plane_cleanup - Clean up the core plane usage 91306fede5aSFrançois Tigeot * @plane: plane to cleanup 91406fede5aSFrançois Tigeot * 91506fede5aSFrançois Tigeot * This function cleans up @plane and removes it from the DRM mode setting 91606fede5aSFrançois Tigeot * core. Note that the function does *not* free the plane structure itself, 91706fede5aSFrançois Tigeot * this is the responsibility of the caller. 91806fede5aSFrançois Tigeot */ 9195718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane) 9205718399fSFrançois Tigeot { 9215718399fSFrançois Tigeot struct drm_device *dev = plane->dev; 9225718399fSFrançois Tigeot 9234dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 9244dbb207bSFrançois Tigeot kfree(plane->format_types); 9255718399fSFrançois Tigeot drm_mode_object_put(dev, &plane->base); 9265718399fSFrançois Tigeot /* if not added to a list, it must be a private plane */ 9275718399fSFrançois Tigeot if (!list_empty(&plane->head)) { 9285718399fSFrançois Tigeot list_del(&plane->head); 9295718399fSFrançois Tigeot dev->mode_config.num_plane--; 9305718399fSFrançois Tigeot } 9314dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 9325718399fSFrançois Tigeot } 933b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup); 9345718399fSFrançois Tigeot 9355718399fSFrançois Tigeot /** 93606fede5aSFrançois Tigeot * drm_plane_force_disable - Forcibly disable a plane 93706fede5aSFrançois Tigeot * @plane: plane to disable 93806fede5aSFrançois Tigeot * 93906fede5aSFrançois Tigeot * Forces the plane to be disabled. 94006fede5aSFrançois Tigeot * 94106fede5aSFrançois Tigeot * Used when the plane's current framebuffer is destroyed, 94206fede5aSFrançois Tigeot * and when restoring fbdev mode. 94306fede5aSFrançois Tigeot */ 94406fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane) 94506fede5aSFrançois Tigeot { 94606fede5aSFrançois Tigeot int ret; 94706fede5aSFrançois Tigeot 94806fede5aSFrançois Tigeot if (!plane->fb) 94906fede5aSFrançois Tigeot return; 95006fede5aSFrançois Tigeot 95106fede5aSFrançois Tigeot ret = plane->funcs->disable_plane(plane); 95206fede5aSFrançois Tigeot if (ret) 95306fede5aSFrançois Tigeot DRM_ERROR("failed to disable plane with busy fb\n"); 95406fede5aSFrançois Tigeot /* disconnect the plane from the fb and crtc: */ 95506fede5aSFrançois Tigeot __drm_framebuffer_unreference(plane->fb); 95606fede5aSFrançois Tigeot plane->fb = NULL; 95706fede5aSFrançois Tigeot plane->crtc = NULL; 95806fede5aSFrançois Tigeot } 95906fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable); 96006fede5aSFrançois Tigeot 96106fede5aSFrançois Tigeot /** 9625718399fSFrançois Tigeot * drm_mode_create - create a new display mode 9635718399fSFrançois Tigeot * @dev: DRM device 9645718399fSFrançois Tigeot * 9655718399fSFrançois Tigeot * Create a new drm_display_mode, give it an ID, and return it. 9665718399fSFrançois Tigeot * 9675718399fSFrançois Tigeot * RETURNS: 9685718399fSFrançois Tigeot * Pointer to new mode on success, NULL on error. 9695718399fSFrançois Tigeot */ 9705718399fSFrançois Tigeot struct drm_display_mode *drm_mode_create(struct drm_device *dev) 9715718399fSFrançois Tigeot { 9725718399fSFrançois Tigeot struct drm_display_mode *nmode; 9735718399fSFrançois Tigeot 9744dbb207bSFrançois Tigeot nmode = kzalloc(sizeof(struct drm_display_mode), GFP_KERNEL); 975b5162e19SFrançois Tigeot if (!nmode) 976b5162e19SFrançois Tigeot return NULL; 9775718399fSFrançois Tigeot 9785718399fSFrançois Tigeot if (drm_mode_object_get(dev, &nmode->base, DRM_MODE_OBJECT_MODE)) { 9794dbb207bSFrançois Tigeot kfree(nmode); 980b5162e19SFrançois Tigeot return NULL; 9815718399fSFrançois Tigeot } 982b5162e19SFrançois Tigeot 9835718399fSFrançois Tigeot return nmode; 9845718399fSFrançois Tigeot } 985b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create); 9865718399fSFrançois Tigeot 9875718399fSFrançois Tigeot /** 9885718399fSFrançois Tigeot * drm_mode_destroy - remove a mode 9895718399fSFrançois Tigeot * @dev: DRM device 9905718399fSFrançois Tigeot * @mode: mode to remove 9915718399fSFrançois Tigeot * 9925718399fSFrançois Tigeot * Free @mode's unique identifier, then free it. 9935718399fSFrançois Tigeot */ 9945718399fSFrançois Tigeot void drm_mode_destroy(struct drm_device *dev, struct drm_display_mode *mode) 9955718399fSFrançois Tigeot { 9965718399fSFrançois Tigeot if (!mode) 9975718399fSFrançois Tigeot return; 9985718399fSFrançois Tigeot 9995718399fSFrançois Tigeot drm_mode_object_put(dev, &mode->base); 10005718399fSFrançois Tigeot 10014dbb207bSFrançois Tigeot kfree(mode); 10025718399fSFrançois Tigeot } 1003b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_destroy); 10045718399fSFrançois Tigeot 10055718399fSFrançois Tigeot static int drm_mode_create_standard_connector_properties(struct drm_device *dev) 10065718399fSFrançois Tigeot { 10075718399fSFrançois Tigeot struct drm_property *edid; 10085718399fSFrançois Tigeot struct drm_property *dpms; 10095718399fSFrançois Tigeot 10105718399fSFrançois Tigeot /* 10115718399fSFrançois Tigeot * Standard properties (apply to all connectors) 10125718399fSFrançois Tigeot */ 10135718399fSFrançois Tigeot edid = drm_property_create(dev, DRM_MODE_PROP_BLOB | 10145718399fSFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 10155718399fSFrançois Tigeot "EDID", 0); 10165718399fSFrançois Tigeot dev->mode_config.edid_property = edid; 10175718399fSFrançois Tigeot 10185718399fSFrançois Tigeot dpms = drm_property_create_enum(dev, 0, 10195718399fSFrançois Tigeot "DPMS", drm_dpms_enum_list, 1020b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dpms_enum_list)); 10215718399fSFrançois Tigeot dev->mode_config.dpms_property = dpms; 10225718399fSFrançois Tigeot 10235718399fSFrançois Tigeot return 0; 10245718399fSFrançois Tigeot } 10255718399fSFrançois Tigeot 10265718399fSFrançois Tigeot /** 10275718399fSFrançois Tigeot * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 10285718399fSFrançois Tigeot * @dev: DRM device 10295718399fSFrançois Tigeot * 10305718399fSFrançois Tigeot * Called by a driver the first time a DVI-I connector is made. 10315718399fSFrançois Tigeot */ 10325718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev) 10335718399fSFrançois Tigeot { 10345718399fSFrançois Tigeot struct drm_property *dvi_i_selector; 10355718399fSFrançois Tigeot struct drm_property *dvi_i_subconnector; 10365718399fSFrançois Tigeot 10375718399fSFrançois Tigeot if (dev->mode_config.dvi_i_select_subconnector_property) 10385718399fSFrançois Tigeot return 0; 10395718399fSFrançois Tigeot 10405718399fSFrançois Tigeot dvi_i_selector = 10415718399fSFrançois Tigeot drm_property_create_enum(dev, 0, 10425718399fSFrançois Tigeot "select subconnector", 10435718399fSFrançois Tigeot drm_dvi_i_select_enum_list, 1044b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_select_enum_list)); 10455718399fSFrançois Tigeot dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 10465718399fSFrançois Tigeot 10475718399fSFrançois Tigeot dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 10485718399fSFrançois Tigeot "subconnector", 10495718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list, 1050b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 10515718399fSFrançois Tigeot dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 10525718399fSFrançois Tigeot 10535718399fSFrançois Tigeot return 0; 10545718399fSFrançois Tigeot } 1055b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); 10565718399fSFrançois Tigeot 10575718399fSFrançois Tigeot /** 10585718399fSFrançois Tigeot * drm_create_tv_properties - create TV specific connector properties 10595718399fSFrançois Tigeot * @dev: DRM device 10605718399fSFrançois Tigeot * @num_modes: number of different TV formats (modes) supported 10615718399fSFrançois Tigeot * @modes: array of pointers to strings containing name of each format 10625718399fSFrançois Tigeot * 10635718399fSFrançois Tigeot * Called by a driver's TV initialization routine, this function creates 10645718399fSFrançois Tigeot * the TV specific connector properties for a given device. Caller is 10655718399fSFrançois Tigeot * responsible for allocating a list of format names and passing them to 10665718399fSFrançois Tigeot * this routine. 10675718399fSFrançois Tigeot */ 10685718399fSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev, int num_modes, 10695718399fSFrançois Tigeot char *modes[]) 10705718399fSFrançois Tigeot { 10715718399fSFrançois Tigeot struct drm_property *tv_selector; 10725718399fSFrançois Tigeot struct drm_property *tv_subconnector; 10735718399fSFrançois Tigeot int i; 10745718399fSFrançois Tigeot 10755718399fSFrançois Tigeot if (dev->mode_config.tv_select_subconnector_property) 10765718399fSFrançois Tigeot return 0; 10775718399fSFrançois Tigeot 10785718399fSFrançois Tigeot /* 10795718399fSFrançois Tigeot * Basic connector properties 10805718399fSFrançois Tigeot */ 10815718399fSFrançois Tigeot tv_selector = drm_property_create_enum(dev, 0, 10825718399fSFrançois Tigeot "select subconnector", 10835718399fSFrançois Tigeot drm_tv_select_enum_list, 1084b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_select_enum_list)); 10855718399fSFrançois Tigeot dev->mode_config.tv_select_subconnector_property = tv_selector; 10865718399fSFrançois Tigeot 10875718399fSFrançois Tigeot tv_subconnector = 10885718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 10895718399fSFrançois Tigeot "subconnector", 10905718399fSFrançois Tigeot drm_tv_subconnector_enum_list, 1091b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_subconnector_enum_list)); 10925718399fSFrançois Tigeot dev->mode_config.tv_subconnector_property = tv_subconnector; 10935718399fSFrançois Tigeot 10945718399fSFrançois Tigeot /* 10955718399fSFrançois Tigeot * Other, TV specific properties: margins & TV modes. 10965718399fSFrançois Tigeot */ 10975718399fSFrançois Tigeot dev->mode_config.tv_left_margin_property = 10985718399fSFrançois Tigeot drm_property_create_range(dev, 0, "left margin", 0, 100); 10995718399fSFrançois Tigeot 11005718399fSFrançois Tigeot dev->mode_config.tv_right_margin_property = 11015718399fSFrançois Tigeot drm_property_create_range(dev, 0, "right margin", 0, 100); 11025718399fSFrançois Tigeot 11035718399fSFrançois Tigeot dev->mode_config.tv_top_margin_property = 11045718399fSFrançois Tigeot drm_property_create_range(dev, 0, "top margin", 0, 100); 11055718399fSFrançois Tigeot 11065718399fSFrançois Tigeot dev->mode_config.tv_bottom_margin_property = 11075718399fSFrançois Tigeot drm_property_create_range(dev, 0, "bottom margin", 0, 100); 11085718399fSFrançois Tigeot 11095718399fSFrançois Tigeot dev->mode_config.tv_mode_property = 11105718399fSFrançois Tigeot drm_property_create(dev, DRM_MODE_PROP_ENUM, 11115718399fSFrançois Tigeot "mode", num_modes); 11125718399fSFrançois Tigeot for (i = 0; i < num_modes; i++) 11135718399fSFrançois Tigeot drm_property_add_enum(dev->mode_config.tv_mode_property, i, 11145718399fSFrançois Tigeot i, modes[i]); 11155718399fSFrançois Tigeot 11165718399fSFrançois Tigeot dev->mode_config.tv_brightness_property = 11175718399fSFrançois Tigeot drm_property_create_range(dev, 0, "brightness", 0, 100); 11185718399fSFrançois Tigeot 11195718399fSFrançois Tigeot dev->mode_config.tv_contrast_property = 11205718399fSFrançois Tigeot drm_property_create_range(dev, 0, "contrast", 0, 100); 11215718399fSFrançois Tigeot 11225718399fSFrançois Tigeot dev->mode_config.tv_flicker_reduction_property = 11235718399fSFrançois Tigeot drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 11245718399fSFrançois Tigeot 11255718399fSFrançois Tigeot dev->mode_config.tv_overscan_property = 11265718399fSFrançois Tigeot drm_property_create_range(dev, 0, "overscan", 0, 100); 11275718399fSFrançois Tigeot 11285718399fSFrançois Tigeot dev->mode_config.tv_saturation_property = 11295718399fSFrançois Tigeot drm_property_create_range(dev, 0, "saturation", 0, 100); 11305718399fSFrançois Tigeot 11315718399fSFrançois Tigeot dev->mode_config.tv_hue_property = 11325718399fSFrançois Tigeot drm_property_create_range(dev, 0, "hue", 0, 100); 11335718399fSFrançois Tigeot 11345718399fSFrançois Tigeot return 0; 11355718399fSFrançois Tigeot } 1136b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties); 11375718399fSFrançois Tigeot 11385718399fSFrançois Tigeot /** 11395718399fSFrançois Tigeot * drm_mode_create_scaling_mode_property - create scaling mode property 11405718399fSFrançois Tigeot * @dev: DRM device 11415718399fSFrançois Tigeot * 11425718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 11435718399fSFrançois Tigeot * connectors. 11445718399fSFrançois Tigeot */ 11455718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev) 11465718399fSFrançois Tigeot { 11475718399fSFrançois Tigeot struct drm_property *scaling_mode; 11485718399fSFrançois Tigeot 11495718399fSFrançois Tigeot if (dev->mode_config.scaling_mode_property) 11505718399fSFrançois Tigeot return 0; 11515718399fSFrançois Tigeot 11525718399fSFrançois Tigeot scaling_mode = 11535718399fSFrançois Tigeot drm_property_create_enum(dev, 0, "scaling mode", 11545718399fSFrançois Tigeot drm_scaling_mode_enum_list, 1155b5162e19SFrançois Tigeot ARRAY_SIZE(drm_scaling_mode_enum_list)); 11565718399fSFrançois Tigeot 11575718399fSFrançois Tigeot dev->mode_config.scaling_mode_property = scaling_mode; 11585718399fSFrançois Tigeot 11595718399fSFrançois Tigeot return 0; 11605718399fSFrançois Tigeot } 1161b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); 11625718399fSFrançois Tigeot 11635718399fSFrançois Tigeot /** 11645718399fSFrançois Tigeot * drm_mode_create_dirty_property - create dirty property 11655718399fSFrançois Tigeot * @dev: DRM device 11665718399fSFrançois Tigeot * 11675718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 11685718399fSFrançois Tigeot * connectors. 11695718399fSFrançois Tigeot */ 11705718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev) 11715718399fSFrançois Tigeot { 11725718399fSFrançois Tigeot struct drm_property *dirty_info; 11735718399fSFrançois Tigeot 11745718399fSFrançois Tigeot if (dev->mode_config.dirty_info_property) 11755718399fSFrançois Tigeot return 0; 11765718399fSFrançois Tigeot 11775718399fSFrançois Tigeot dirty_info = 11785718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 11795718399fSFrançois Tigeot "dirty", 11805718399fSFrançois Tigeot drm_dirty_info_enum_list, 1181b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dirty_info_enum_list)); 11825718399fSFrançois Tigeot dev->mode_config.dirty_info_property = dirty_info; 11835718399fSFrançois Tigeot 11845718399fSFrançois Tigeot return 0; 11855718399fSFrançois Tigeot } 1186b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property); 11875718399fSFrançois Tigeot 1188b5162e19SFrançois Tigeot static int drm_mode_group_init(struct drm_device *dev, struct drm_mode_group *group) 11895718399fSFrançois Tigeot { 11905718399fSFrançois Tigeot uint32_t total_objects = 0; 11915718399fSFrançois Tigeot 11925718399fSFrançois Tigeot total_objects += dev->mode_config.num_crtc; 11935718399fSFrançois Tigeot total_objects += dev->mode_config.num_connector; 11945718399fSFrançois Tigeot total_objects += dev->mode_config.num_encoder; 11955718399fSFrançois Tigeot 11964dbb207bSFrançois Tigeot group->id_list = kzalloc(total_objects * sizeof(uint32_t), GFP_KERNEL); 1197b5162e19SFrançois Tigeot if (!group->id_list) 1198b5162e19SFrançois Tigeot return -ENOMEM; 11995718399fSFrançois Tigeot 12005718399fSFrançois Tigeot group->num_crtcs = 0; 12015718399fSFrançois Tigeot group->num_connectors = 0; 12025718399fSFrançois Tigeot group->num_encoders = 0; 12035718399fSFrançois Tigeot return 0; 12045718399fSFrançois Tigeot } 12055718399fSFrançois Tigeot 12065718399fSFrançois Tigeot int drm_mode_group_init_legacy_group(struct drm_device *dev, 12075718399fSFrançois Tigeot struct drm_mode_group *group) 12085718399fSFrançois Tigeot { 12095718399fSFrançois Tigeot struct drm_crtc *crtc; 12105718399fSFrançois Tigeot struct drm_encoder *encoder; 12115718399fSFrançois Tigeot struct drm_connector *connector; 12125718399fSFrançois Tigeot int ret; 12135718399fSFrançois Tigeot 12145718399fSFrançois Tigeot if ((ret = drm_mode_group_init(dev, group))) 12155718399fSFrançois Tigeot return ret; 12165718399fSFrançois Tigeot 12175718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 12185718399fSFrançois Tigeot group->id_list[group->num_crtcs++] = crtc->base.id; 12195718399fSFrançois Tigeot 12205718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 12215718399fSFrançois Tigeot group->id_list[group->num_crtcs + group->num_encoders++] = 12225718399fSFrançois Tigeot encoder->base.id; 12235718399fSFrançois Tigeot 12245718399fSFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 12255718399fSFrançois Tigeot group->id_list[group->num_crtcs + group->num_encoders + 12265718399fSFrançois Tigeot group->num_connectors++] = connector->base.id; 12275718399fSFrançois Tigeot 12285718399fSFrançois Tigeot return 0; 12295718399fSFrançois Tigeot } 1230b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_group_init_legacy_group); 12315718399fSFrançois Tigeot 12325718399fSFrançois Tigeot /** 12335718399fSFrançois Tigeot * drm_crtc_convert_to_umode - convert a drm_display_mode into a modeinfo 12345718399fSFrançois Tigeot * @out: drm_mode_modeinfo struct to return to the user 12355718399fSFrançois Tigeot * @in: drm_display_mode to use 12365718399fSFrançois Tigeot * 12375718399fSFrançois Tigeot * Convert a drm_display_mode into a drm_mode_modeinfo structure to return to 12385718399fSFrançois Tigeot * the user. 12395718399fSFrançois Tigeot */ 12405718399fSFrançois Tigeot static void drm_crtc_convert_to_umode(struct drm_mode_modeinfo *out, 12415718399fSFrançois Tigeot const struct drm_display_mode *in) 12425718399fSFrançois Tigeot { 1243b5162e19SFrançois Tigeot WARN(in->hdisplay > USHRT_MAX || in->hsync_start > USHRT_MAX || 12445718399fSFrançois Tigeot in->hsync_end > USHRT_MAX || in->htotal > USHRT_MAX || 12455718399fSFrançois Tigeot in->hskew > USHRT_MAX || in->vdisplay > USHRT_MAX || 12465718399fSFrançois Tigeot in->vsync_start > USHRT_MAX || in->vsync_end > USHRT_MAX || 1247b5162e19SFrançois Tigeot in->vtotal > USHRT_MAX || in->vscan > USHRT_MAX, 1248b5162e19SFrançois Tigeot "timing values too large for mode info\n"); 12495718399fSFrançois Tigeot 12505718399fSFrançois Tigeot out->clock = in->clock; 12515718399fSFrançois Tigeot out->hdisplay = in->hdisplay; 12525718399fSFrançois Tigeot out->hsync_start = in->hsync_start; 12535718399fSFrançois Tigeot out->hsync_end = in->hsync_end; 12545718399fSFrançois Tigeot out->htotal = in->htotal; 12555718399fSFrançois Tigeot out->hskew = in->hskew; 12565718399fSFrançois Tigeot out->vdisplay = in->vdisplay; 12575718399fSFrançois Tigeot out->vsync_start = in->vsync_start; 12585718399fSFrançois Tigeot out->vsync_end = in->vsync_end; 12595718399fSFrançois Tigeot out->vtotal = in->vtotal; 12605718399fSFrançois Tigeot out->vscan = in->vscan; 12615718399fSFrançois Tigeot out->vrefresh = in->vrefresh; 12625718399fSFrançois Tigeot out->flags = in->flags; 12635718399fSFrançois Tigeot out->type = in->type; 12645718399fSFrançois Tigeot strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 12655718399fSFrançois Tigeot out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 12665718399fSFrançois Tigeot } 12675718399fSFrançois Tigeot 12685718399fSFrançois Tigeot /** 1269*9edbd4a0SFrançois Tigeot * drm_crtc_convert_umode - convert a modeinfo into a drm_display_mode 12705718399fSFrançois Tigeot * @out: drm_display_mode to return to the user 12715718399fSFrançois Tigeot * @in: drm_mode_modeinfo to use 12725718399fSFrançois Tigeot * 12735718399fSFrançois Tigeot * Convert a drm_mode_modeinfo into a drm_display_mode structure to return to 12745718399fSFrançois Tigeot * the caller. 12755718399fSFrançois Tigeot * 12765718399fSFrançois Tigeot * RETURNS: 12775718399fSFrançois Tigeot * Zero on success, errno on failure. 12785718399fSFrançois Tigeot */ 12795718399fSFrançois Tigeot static int drm_crtc_convert_umode(struct drm_display_mode *out, 12805718399fSFrançois Tigeot const struct drm_mode_modeinfo *in) 12815718399fSFrançois Tigeot { 12825718399fSFrançois Tigeot if (in->clock > INT_MAX || in->vrefresh > INT_MAX) 1283b5162e19SFrançois Tigeot return -ERANGE; 12845718399fSFrançois Tigeot 1285*9edbd4a0SFrançois Tigeot if ((in->flags & DRM_MODE_FLAG_3D_MASK) > DRM_MODE_FLAG_3D_MAX) 1286*9edbd4a0SFrançois Tigeot return -EINVAL; 1287*9edbd4a0SFrançois Tigeot 12885718399fSFrançois Tigeot out->clock = in->clock; 12895718399fSFrançois Tigeot out->hdisplay = in->hdisplay; 12905718399fSFrançois Tigeot out->hsync_start = in->hsync_start; 12915718399fSFrançois Tigeot out->hsync_end = in->hsync_end; 12925718399fSFrançois Tigeot out->htotal = in->htotal; 12935718399fSFrançois Tigeot out->hskew = in->hskew; 12945718399fSFrançois Tigeot out->vdisplay = in->vdisplay; 12955718399fSFrançois Tigeot out->vsync_start = in->vsync_start; 12965718399fSFrançois Tigeot out->vsync_end = in->vsync_end; 12975718399fSFrançois Tigeot out->vtotal = in->vtotal; 12985718399fSFrançois Tigeot out->vscan = in->vscan; 12995718399fSFrançois Tigeot out->vrefresh = in->vrefresh; 13005718399fSFrançois Tigeot out->flags = in->flags; 13015718399fSFrançois Tigeot out->type = in->type; 13025718399fSFrançois Tigeot strncpy(out->name, in->name, DRM_DISPLAY_MODE_LEN); 13035718399fSFrançois Tigeot out->name[DRM_DISPLAY_MODE_LEN-1] = 0; 13045718399fSFrançois Tigeot 13055718399fSFrançois Tigeot return 0; 13065718399fSFrançois Tigeot } 13075718399fSFrançois Tigeot 13085718399fSFrançois Tigeot /** 13095718399fSFrançois Tigeot * drm_mode_getresources - get graphics configuration 13104dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 13114dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 13124dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 13135718399fSFrançois Tigeot * 13145718399fSFrançois Tigeot * Construct a set of configuration description structures and return 13155718399fSFrançois Tigeot * them to the user, including CRTC, connector and framebuffer configuration. 13165718399fSFrançois Tigeot * 13175718399fSFrançois Tigeot * Called by the user via ioctl. 13185718399fSFrançois Tigeot * 13195718399fSFrançois Tigeot * RETURNS: 13205718399fSFrançois Tigeot * Zero on success, errno on failure. 13215718399fSFrançois Tigeot */ 13225718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data, 13235718399fSFrançois Tigeot struct drm_file *file_priv) 13245718399fSFrançois Tigeot { 13255718399fSFrançois Tigeot struct drm_mode_card_res *card_res = data; 13265718399fSFrançois Tigeot struct list_head *lh; 13275718399fSFrançois Tigeot struct drm_framebuffer *fb; 13285718399fSFrançois Tigeot struct drm_connector *connector; 13295718399fSFrançois Tigeot struct drm_crtc *crtc; 13305718399fSFrançois Tigeot struct drm_encoder *encoder; 13315718399fSFrançois Tigeot int ret = 0; 13325718399fSFrançois Tigeot int connector_count = 0; 13335718399fSFrançois Tigeot int crtc_count = 0; 13345718399fSFrançois Tigeot int fb_count = 0; 13355718399fSFrançois Tigeot int encoder_count = 0; 13365718399fSFrançois Tigeot int copied = 0, i; 13375718399fSFrançois Tigeot uint32_t __user *fb_id; 13385718399fSFrançois Tigeot uint32_t __user *crtc_id; 13395718399fSFrançois Tigeot uint32_t __user *connector_id; 13405718399fSFrançois Tigeot uint32_t __user *encoder_id; 13415718399fSFrançois Tigeot struct drm_mode_group *mode_group; 13425718399fSFrançois Tigeot 13435718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1344b5162e19SFrançois Tigeot return -EINVAL; 13455718399fSFrançois Tigeot 13465718399fSFrançois Tigeot 13474dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 13485718399fSFrançois Tigeot /* 13495718399fSFrançois Tigeot * For the non-control nodes we need to limit the list of resources 13505718399fSFrançois Tigeot * by IDs in the group list for this node 13515718399fSFrançois Tigeot */ 13525718399fSFrançois Tigeot list_for_each(lh, &file_priv->fbs) 13535718399fSFrançois Tigeot fb_count++; 13545718399fSFrançois Tigeot 13554dbb207bSFrançois Tigeot /* handle this in 4 parts */ 13564dbb207bSFrançois Tigeot /* FBs */ 13574dbb207bSFrançois Tigeot if (card_res->count_fbs >= fb_count) { 13584dbb207bSFrançois Tigeot copied = 0; 13594dbb207bSFrançois Tigeot fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; 13604dbb207bSFrançois Tigeot list_for_each_entry(fb, &file_priv->fbs, filp_head) { 13614dbb207bSFrançois Tigeot if (put_user(fb->base.id, fb_id + copied)) { 13624dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 13634dbb207bSFrançois Tigeot return -EFAULT; 13644dbb207bSFrançois Tigeot } 13654dbb207bSFrançois Tigeot copied++; 13664dbb207bSFrançois Tigeot } 13674dbb207bSFrançois Tigeot } 13684dbb207bSFrançois Tigeot card_res->count_fbs = fb_count; 13694dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 13704dbb207bSFrançois Tigeot 13714dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 13724dbb207bSFrançois Tigeot #if 0 13734dbb207bSFrançois Tigeot mode_group = &file_priv->master->minor->mode_group; 13744dbb207bSFrançois Tigeot if (file_priv->master->minor->type == DRM_MINOR_CONTROL) { 13754dbb207bSFrançois Tigeot #else 13765718399fSFrançois Tigeot mode_group = NULL; /* XXXKIB */ 13775718399fSFrançois Tigeot if (1 || file_priv->master) { 13785718399fSFrançois Tigeot #endif 13795718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.crtc_list) 13805718399fSFrançois Tigeot crtc_count++; 13815718399fSFrançois Tigeot 13825718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.connector_list) 13835718399fSFrançois Tigeot connector_count++; 13845718399fSFrançois Tigeot 13855718399fSFrançois Tigeot list_for_each(lh, &dev->mode_config.encoder_list) 13865718399fSFrançois Tigeot encoder_count++; 13875718399fSFrançois Tigeot } else { 13885718399fSFrançois Tigeot 13895718399fSFrançois Tigeot crtc_count = mode_group->num_crtcs; 13905718399fSFrançois Tigeot connector_count = mode_group->num_connectors; 13915718399fSFrançois Tigeot encoder_count = mode_group->num_encoders; 13925718399fSFrançois Tigeot } 13935718399fSFrançois Tigeot 13945718399fSFrançois Tigeot card_res->max_height = dev->mode_config.max_height; 13955718399fSFrançois Tigeot card_res->min_height = dev->mode_config.min_height; 13965718399fSFrançois Tigeot card_res->max_width = dev->mode_config.max_width; 13975718399fSFrançois Tigeot card_res->min_width = dev->mode_config.min_width; 13985718399fSFrançois Tigeot 13995718399fSFrançois Tigeot /* CRTCs */ 14005718399fSFrançois Tigeot if (card_res->count_crtcs >= crtc_count) { 14015718399fSFrançois Tigeot copied = 0; 1402b5162e19SFrançois Tigeot crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; 14034dbb207bSFrançois Tigeot if (1 || file_priv->master) { 14045718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, 14055718399fSFrançois Tigeot head) { 14065718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 1407b5162e19SFrançois Tigeot if (put_user(crtc->base.id, crtc_id + copied)) { 1408b5162e19SFrançois Tigeot ret = -EFAULT; 14095718399fSFrançois Tigeot goto out; 14105718399fSFrançois Tigeot } 14115718399fSFrançois Tigeot copied++; 14125718399fSFrançois Tigeot } 14135718399fSFrançois Tigeot } else { 14145718399fSFrançois Tigeot for (i = 0; i < mode_group->num_crtcs; i++) { 1415b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1416b5162e19SFrançois Tigeot crtc_id + copied)) { 1417b5162e19SFrançois Tigeot ret = -EFAULT; 14185718399fSFrançois Tigeot goto out; 14195718399fSFrançois Tigeot } 14205718399fSFrançois Tigeot copied++; 14215718399fSFrançois Tigeot } 14225718399fSFrançois Tigeot } 14235718399fSFrançois Tigeot } 14245718399fSFrançois Tigeot card_res->count_crtcs = crtc_count; 14255718399fSFrançois Tigeot 14265718399fSFrançois Tigeot /* Encoders */ 14275718399fSFrançois Tigeot if (card_res->count_encoders >= encoder_count) { 14285718399fSFrançois Tigeot copied = 0; 1429b5162e19SFrançois Tigeot encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; 14304dbb207bSFrançois Tigeot if (file_priv->master) { 14315718399fSFrançois Tigeot list_for_each_entry(encoder, 14325718399fSFrançois Tigeot &dev->mode_config.encoder_list, 14335718399fSFrançois Tigeot head) { 14345718399fSFrançois Tigeot DRM_DEBUG_KMS("[ENCODER:%d:%s]\n", encoder->base.id, 14355718399fSFrançois Tigeot drm_get_encoder_name(encoder)); 1436b5162e19SFrançois Tigeot if (put_user(encoder->base.id, encoder_id + 1437b5162e19SFrançois Tigeot copied)) { 1438b5162e19SFrançois Tigeot ret = -EFAULT; 14395718399fSFrançois Tigeot goto out; 14405718399fSFrançois Tigeot } 14415718399fSFrançois Tigeot copied++; 14425718399fSFrançois Tigeot } 14435718399fSFrançois Tigeot } else { 1444b5162e19SFrançois Tigeot for (i = mode_group->num_crtcs; i < mode_group->num_crtcs + mode_group->num_encoders; i++) { 1445b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1446b5162e19SFrançois Tigeot encoder_id + copied)) { 1447b5162e19SFrançois Tigeot ret = -EFAULT; 14485718399fSFrançois Tigeot goto out; 14495718399fSFrançois Tigeot } 14505718399fSFrançois Tigeot copied++; 14515718399fSFrançois Tigeot } 14525718399fSFrançois Tigeot 14535718399fSFrançois Tigeot } 14545718399fSFrançois Tigeot } 14555718399fSFrançois Tigeot card_res->count_encoders = encoder_count; 14565718399fSFrançois Tigeot 14575718399fSFrançois Tigeot /* Connectors */ 14585718399fSFrançois Tigeot if (card_res->count_connectors >= connector_count) { 14595718399fSFrançois Tigeot copied = 0; 1460b5162e19SFrançois Tigeot connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; 14614dbb207bSFrançois Tigeot if (file_priv->master) { 14625718399fSFrançois Tigeot list_for_each_entry(connector, 14635718399fSFrançois Tigeot &dev->mode_config.connector_list, 14645718399fSFrançois Tigeot head) { 14655718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 14665718399fSFrançois Tigeot connector->base.id, 14675718399fSFrançois Tigeot drm_get_connector_name(connector)); 1468b5162e19SFrançois Tigeot if (put_user(connector->base.id, 1469b5162e19SFrançois Tigeot connector_id + copied)) { 1470b5162e19SFrançois Tigeot ret = -EFAULT; 14715718399fSFrançois Tigeot goto out; 14725718399fSFrançois Tigeot } 14735718399fSFrançois Tigeot copied++; 14745718399fSFrançois Tigeot } 14755718399fSFrançois Tigeot } else { 14765718399fSFrançois Tigeot int start = mode_group->num_crtcs + 14775718399fSFrançois Tigeot mode_group->num_encoders; 14785718399fSFrançois Tigeot for (i = start; i < start + mode_group->num_connectors; i++) { 1479b5162e19SFrançois Tigeot if (put_user(mode_group->id_list[i], 1480b5162e19SFrançois Tigeot connector_id + copied)) { 1481b5162e19SFrançois Tigeot ret = -EFAULT; 14825718399fSFrançois Tigeot goto out; 14835718399fSFrançois Tigeot } 14845718399fSFrançois Tigeot copied++; 14855718399fSFrançois Tigeot } 14865718399fSFrançois Tigeot } 14875718399fSFrançois Tigeot } 14885718399fSFrançois Tigeot card_res->count_connectors = connector_count; 14895718399fSFrançois Tigeot 14905718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC[%d] CONNECTORS[%d] ENCODERS[%d]\n", card_res->count_crtcs, 14915718399fSFrançois Tigeot card_res->count_connectors, card_res->count_encoders); 14925718399fSFrançois Tigeot 14935718399fSFrançois Tigeot out: 14944dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 14955718399fSFrançois Tigeot return ret; 14965718399fSFrançois Tigeot } 14975718399fSFrançois Tigeot 14985718399fSFrançois Tigeot /** 14995718399fSFrançois Tigeot * drm_mode_getcrtc - get CRTC configuration 15004dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 15014dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 15024dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 15035718399fSFrançois Tigeot * 15045718399fSFrançois Tigeot * Construct a CRTC configuration structure to return to the user. 15055718399fSFrançois Tigeot * 15065718399fSFrançois Tigeot * Called by the user via ioctl. 15075718399fSFrançois Tigeot * 15085718399fSFrançois Tigeot * RETURNS: 15095718399fSFrançois Tigeot * Zero on success, errno on failure. 15105718399fSFrançois Tigeot */ 15115718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev, 15125718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 15135718399fSFrançois Tigeot { 15145718399fSFrançois Tigeot struct drm_mode_crtc *crtc_resp = data; 15155718399fSFrançois Tigeot struct drm_crtc *crtc; 15165718399fSFrançois Tigeot struct drm_mode_object *obj; 15175718399fSFrançois Tigeot int ret = 0; 15185718399fSFrançois Tigeot 15195718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1520b5162e19SFrançois Tigeot return -EINVAL; 15215718399fSFrançois Tigeot 15224dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 15235718399fSFrançois Tigeot 15245718399fSFrançois Tigeot obj = drm_mode_object_find(dev, crtc_resp->crtc_id, 15255718399fSFrançois Tigeot DRM_MODE_OBJECT_CRTC); 15265718399fSFrançois Tigeot if (!obj) { 1527*9edbd4a0SFrançois Tigeot ret = -ENOENT; 15285718399fSFrançois Tigeot goto out; 15295718399fSFrançois Tigeot } 15305718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 15315718399fSFrançois Tigeot 15325718399fSFrançois Tigeot crtc_resp->x = crtc->x; 15335718399fSFrançois Tigeot crtc_resp->y = crtc->y; 15345718399fSFrançois Tigeot crtc_resp->gamma_size = crtc->gamma_size; 15355718399fSFrançois Tigeot if (crtc->fb) 15365718399fSFrançois Tigeot crtc_resp->fb_id = crtc->fb->base.id; 15375718399fSFrançois Tigeot else 15385718399fSFrançois Tigeot crtc_resp->fb_id = 0; 15395718399fSFrançois Tigeot 15405718399fSFrançois Tigeot if (crtc->enabled) { 15415718399fSFrançois Tigeot 15425718399fSFrançois Tigeot drm_crtc_convert_to_umode(&crtc_resp->mode, &crtc->mode); 15435718399fSFrançois Tigeot crtc_resp->mode_valid = 1; 15445718399fSFrançois Tigeot 15455718399fSFrançois Tigeot } else { 15465718399fSFrançois Tigeot crtc_resp->mode_valid = 0; 15475718399fSFrançois Tigeot } 15485718399fSFrançois Tigeot 15495718399fSFrançois Tigeot out: 15504dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 15515718399fSFrançois Tigeot return ret; 15525718399fSFrançois Tigeot } 15535718399fSFrançois Tigeot 1554*9edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, 1555*9edbd4a0SFrançois Tigeot const struct drm_file *file_priv) 1556*9edbd4a0SFrançois Tigeot { 1557*9edbd4a0SFrançois Tigeot /* 1558*9edbd4a0SFrançois Tigeot * If user-space hasn't configured the driver to expose the stereo 3D 1559*9edbd4a0SFrançois Tigeot * modes, don't expose them. 1560*9edbd4a0SFrançois Tigeot */ 1561*9edbd4a0SFrançois Tigeot if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) 1562*9edbd4a0SFrançois Tigeot return false; 1563*9edbd4a0SFrançois Tigeot 1564*9edbd4a0SFrançois Tigeot return true; 1565*9edbd4a0SFrançois Tigeot } 1566*9edbd4a0SFrançois Tigeot 15675718399fSFrançois Tigeot /** 15685718399fSFrançois Tigeot * drm_mode_getconnector - get connector configuration 15694dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 15704dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 15714dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 15725718399fSFrançois Tigeot * 15735718399fSFrançois Tigeot * Construct a connector configuration structure to return to the user. 15745718399fSFrançois Tigeot * 15755718399fSFrançois Tigeot * Called by the user via ioctl. 15765718399fSFrançois Tigeot * 15775718399fSFrançois Tigeot * RETURNS: 15785718399fSFrançois Tigeot * Zero on success, errno on failure. 15795718399fSFrançois Tigeot */ 15805718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data, 15815718399fSFrançois Tigeot struct drm_file *file_priv) 15825718399fSFrançois Tigeot { 15835718399fSFrançois Tigeot struct drm_mode_get_connector *out_resp = data; 15845718399fSFrançois Tigeot struct drm_mode_object *obj; 15855718399fSFrançois Tigeot struct drm_connector *connector; 15865718399fSFrançois Tigeot struct drm_display_mode *mode; 15875718399fSFrançois Tigeot int mode_count = 0; 15885718399fSFrançois Tigeot int props_count = 0; 15895718399fSFrançois Tigeot int encoders_count = 0; 15905718399fSFrançois Tigeot int ret = 0; 15915718399fSFrançois Tigeot int copied = 0; 15925718399fSFrançois Tigeot int i; 15935718399fSFrançois Tigeot struct drm_mode_modeinfo u_mode; 15945718399fSFrançois Tigeot struct drm_mode_modeinfo __user *mode_ptr; 1595b5162e19SFrançois Tigeot uint32_t __user *prop_ptr; 1596b5162e19SFrançois Tigeot uint64_t __user *prop_values; 1597b5162e19SFrançois Tigeot uint32_t __user *encoder_ptr; 15985718399fSFrançois Tigeot 15995718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1600b5162e19SFrançois Tigeot return -EINVAL; 16015718399fSFrançois Tigeot 16025718399fSFrançois Tigeot memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 16035718399fSFrançois Tigeot 16045718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:?]\n", out_resp->connector_id); 16055718399fSFrançois Tigeot 16064dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 16075718399fSFrançois Tigeot 16085718399fSFrançois Tigeot obj = drm_mode_object_find(dev, out_resp->connector_id, 16095718399fSFrançois Tigeot DRM_MODE_OBJECT_CONNECTOR); 16105718399fSFrançois Tigeot if (!obj) { 1611*9edbd4a0SFrançois Tigeot ret = -ENOENT; 16125718399fSFrançois Tigeot goto out; 16135718399fSFrançois Tigeot } 16145718399fSFrançois Tigeot connector = obj_to_connector(obj); 16155718399fSFrançois Tigeot 1616b5162e19SFrançois Tigeot props_count = connector->properties.count; 16175718399fSFrançois Tigeot 16185718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 16195718399fSFrançois Tigeot if (connector->encoder_ids[i] != 0) { 16205718399fSFrançois Tigeot encoders_count++; 16215718399fSFrançois Tigeot } 16225718399fSFrançois Tigeot } 16235718399fSFrançois Tigeot 16245718399fSFrançois Tigeot if (out_resp->count_modes == 0) { 16255718399fSFrançois Tigeot connector->funcs->fill_modes(connector, 16265718399fSFrançois Tigeot dev->mode_config.max_width, 16275718399fSFrançois Tigeot dev->mode_config.max_height); 16285718399fSFrançois Tigeot } 16295718399fSFrançois Tigeot 16305718399fSFrançois Tigeot /* delayed so we get modes regardless of pre-fill_modes state */ 16315718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) 1632*9edbd4a0SFrançois Tigeot if (drm_mode_expose_to_userspace(mode, file_priv)) 16335718399fSFrançois Tigeot mode_count++; 16345718399fSFrançois Tigeot 16355718399fSFrançois Tigeot out_resp->connector_id = connector->base.id; 16365718399fSFrançois Tigeot out_resp->connector_type = connector->connector_type; 16375718399fSFrançois Tigeot out_resp->connector_type_id = connector->connector_type_id; 16385718399fSFrançois Tigeot out_resp->mm_width = connector->display_info.width_mm; 16395718399fSFrançois Tigeot out_resp->mm_height = connector->display_info.height_mm; 16405718399fSFrançois Tigeot out_resp->subpixel = connector->display_info.subpixel_order; 16415718399fSFrançois Tigeot out_resp->connection = connector->status; 16425718399fSFrançois Tigeot if (connector->encoder) 16435718399fSFrançois Tigeot out_resp->encoder_id = connector->encoder->base.id; 16445718399fSFrançois Tigeot else 16455718399fSFrançois Tigeot out_resp->encoder_id = 0; 16465718399fSFrançois Tigeot 16475718399fSFrançois Tigeot /* 16485718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 16495718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 16505718399fSFrançois Tigeot */ 16515718399fSFrançois Tigeot if ((out_resp->count_modes >= mode_count) && mode_count) { 16525718399fSFrançois Tigeot copied = 0; 1653b5162e19SFrançois Tigeot mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; 16545718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) { 1655*9edbd4a0SFrançois Tigeot if (!drm_mode_expose_to_userspace(mode, file_priv)) 1656*9edbd4a0SFrançois Tigeot continue; 1657*9edbd4a0SFrançois Tigeot 16585718399fSFrançois Tigeot drm_crtc_convert_to_umode(&u_mode, mode); 1659b5162e19SFrançois Tigeot if (copy_to_user(mode_ptr + copied, 1660b5162e19SFrançois Tigeot &u_mode, sizeof(u_mode))) { 1661b5162e19SFrançois Tigeot ret = -EFAULT; 16625718399fSFrançois Tigeot goto out; 16635718399fSFrançois Tigeot } 16645718399fSFrançois Tigeot copied++; 16655718399fSFrançois Tigeot } 16665718399fSFrançois Tigeot } 16675718399fSFrançois Tigeot out_resp->count_modes = mode_count; 16685718399fSFrançois Tigeot 16695718399fSFrançois Tigeot if ((out_resp->count_props >= props_count) && props_count) { 16705718399fSFrançois Tigeot copied = 0; 1671b5162e19SFrançois Tigeot prop_ptr = (uint32_t __user *)(unsigned long)(out_resp->props_ptr); 1672b5162e19SFrançois Tigeot prop_values = (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr); 1673b5162e19SFrançois Tigeot for (i = 0; i < connector->properties.count; i++) { 1674b5162e19SFrançois Tigeot if (put_user(connector->properties.ids[i], 1675b5162e19SFrançois Tigeot prop_ptr + copied)) { 1676b5162e19SFrançois Tigeot ret = -EFAULT; 16775718399fSFrançois Tigeot goto out; 16785718399fSFrançois Tigeot } 16795718399fSFrançois Tigeot 1680b5162e19SFrançois Tigeot if (put_user(connector->properties.values[i], 1681b5162e19SFrançois Tigeot prop_values + copied)) { 1682b5162e19SFrançois Tigeot ret = -EFAULT; 16835718399fSFrançois Tigeot goto out; 16845718399fSFrançois Tigeot } 16855718399fSFrançois Tigeot copied++; 16865718399fSFrançois Tigeot } 16875718399fSFrançois Tigeot } 16885718399fSFrançois Tigeot out_resp->count_props = props_count; 16895718399fSFrançois Tigeot 16905718399fSFrançois Tigeot if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 16915718399fSFrançois Tigeot copied = 0; 1692b5162e19SFrançois Tigeot encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); 16935718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 16945718399fSFrançois Tigeot if (connector->encoder_ids[i] != 0) { 1695b5162e19SFrançois Tigeot if (put_user(connector->encoder_ids[i], 1696b5162e19SFrançois Tigeot encoder_ptr + copied)) { 1697b5162e19SFrançois Tigeot ret = -EFAULT; 16985718399fSFrançois Tigeot goto out; 16995718399fSFrançois Tigeot } 17005718399fSFrançois Tigeot copied++; 17015718399fSFrançois Tigeot } 17025718399fSFrançois Tigeot } 17035718399fSFrançois Tigeot } 17045718399fSFrançois Tigeot out_resp->count_encoders = encoders_count; 17055718399fSFrançois Tigeot 17065718399fSFrançois Tigeot out: 17074dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 17084dbb207bSFrançois Tigeot 17095718399fSFrançois Tigeot return ret; 17105718399fSFrançois Tigeot } 17115718399fSFrançois Tigeot 17125718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data, 17135718399fSFrançois Tigeot struct drm_file *file_priv) 17145718399fSFrançois Tigeot { 17155718399fSFrançois Tigeot struct drm_mode_get_encoder *enc_resp = data; 17165718399fSFrançois Tigeot struct drm_mode_object *obj; 17175718399fSFrançois Tigeot struct drm_encoder *encoder; 17185718399fSFrançois Tigeot int ret = 0; 17195718399fSFrançois Tigeot 17205718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1721b5162e19SFrançois Tigeot return -EINVAL; 17225718399fSFrançois Tigeot 17234dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 17245718399fSFrançois Tigeot obj = drm_mode_object_find(dev, enc_resp->encoder_id, 17255718399fSFrançois Tigeot DRM_MODE_OBJECT_ENCODER); 17265718399fSFrançois Tigeot if (!obj) { 1727*9edbd4a0SFrançois Tigeot ret = -ENOENT; 17285718399fSFrançois Tigeot goto out; 17295718399fSFrançois Tigeot } 17305718399fSFrançois Tigeot encoder = obj_to_encoder(obj); 17315718399fSFrançois Tigeot 17325718399fSFrançois Tigeot if (encoder->crtc) 17335718399fSFrançois Tigeot enc_resp->crtc_id = encoder->crtc->base.id; 17345718399fSFrançois Tigeot else 17355718399fSFrançois Tigeot enc_resp->crtc_id = 0; 17365718399fSFrançois Tigeot enc_resp->encoder_type = encoder->encoder_type; 17375718399fSFrançois Tigeot enc_resp->encoder_id = encoder->base.id; 17385718399fSFrançois Tigeot enc_resp->possible_crtcs = encoder->possible_crtcs; 17395718399fSFrançois Tigeot enc_resp->possible_clones = encoder->possible_clones; 17405718399fSFrançois Tigeot 17415718399fSFrançois Tigeot out: 17424dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 17435718399fSFrançois Tigeot return ret; 17445718399fSFrançois Tigeot } 17455718399fSFrançois Tigeot 17465718399fSFrançois Tigeot /** 17475718399fSFrançois Tigeot * drm_mode_getplane_res - get plane info 17485718399fSFrançois Tigeot * @dev: DRM device 17495718399fSFrançois Tigeot * @data: ioctl data 17505718399fSFrançois Tigeot * @file_priv: DRM file info 17515718399fSFrançois Tigeot * 17525718399fSFrançois Tigeot * Return an plane count and set of IDs. 17535718399fSFrançois Tigeot */ 17545718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data, 17555718399fSFrançois Tigeot struct drm_file *file_priv) 17565718399fSFrançois Tigeot { 17575718399fSFrançois Tigeot struct drm_mode_get_plane_res *plane_resp = data; 17585718399fSFrançois Tigeot struct drm_mode_config *config; 17595718399fSFrançois Tigeot struct drm_plane *plane; 1760b5162e19SFrançois Tigeot uint32_t __user *plane_ptr; 17615718399fSFrançois Tigeot int copied = 0, ret = 0; 17625718399fSFrançois Tigeot 17635718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1764b5162e19SFrançois Tigeot return -EINVAL; 17655718399fSFrançois Tigeot 17664dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 17675718399fSFrançois Tigeot config = &dev->mode_config; 17685718399fSFrançois Tigeot 17695718399fSFrançois Tigeot /* 17705718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 17715718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 17725718399fSFrançois Tigeot */ 17735718399fSFrançois Tigeot if (config->num_plane && 17745718399fSFrançois Tigeot (plane_resp->count_planes >= config->num_plane)) { 1775b5162e19SFrançois Tigeot plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; 17765718399fSFrançois Tigeot 17775718399fSFrançois Tigeot list_for_each_entry(plane, &config->plane_list, head) { 1778b5162e19SFrançois Tigeot if (put_user(plane->base.id, plane_ptr + copied)) { 1779b5162e19SFrançois Tigeot ret = -EFAULT; 17805718399fSFrançois Tigeot goto out; 17815718399fSFrançois Tigeot } 17825718399fSFrançois Tigeot copied++; 17835718399fSFrançois Tigeot } 17845718399fSFrançois Tigeot } 17855718399fSFrançois Tigeot plane_resp->count_planes = config->num_plane; 17865718399fSFrançois Tigeot 17875718399fSFrançois Tigeot out: 17884dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 17895718399fSFrançois Tigeot return ret; 17905718399fSFrançois Tigeot } 17915718399fSFrançois Tigeot 17925718399fSFrançois Tigeot /** 17935718399fSFrançois Tigeot * drm_mode_getplane - get plane info 17945718399fSFrançois Tigeot * @dev: DRM device 17955718399fSFrançois Tigeot * @data: ioctl data 17965718399fSFrançois Tigeot * @file_priv: DRM file info 17975718399fSFrançois Tigeot * 17985718399fSFrançois Tigeot * Return plane info, including formats supported, gamma size, any 17995718399fSFrançois Tigeot * current fb, etc. 18005718399fSFrançois Tigeot */ 18015718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data, 18025718399fSFrançois Tigeot struct drm_file *file_priv) 18035718399fSFrançois Tigeot { 18045718399fSFrançois Tigeot struct drm_mode_get_plane *plane_resp = data; 18055718399fSFrançois Tigeot struct drm_mode_object *obj; 18065718399fSFrançois Tigeot struct drm_plane *plane; 1807b5162e19SFrançois Tigeot uint32_t __user *format_ptr; 18085718399fSFrançois Tigeot int ret = 0; 18095718399fSFrançois Tigeot 18105718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1811b5162e19SFrançois Tigeot return -EINVAL; 18125718399fSFrançois Tigeot 18134dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 18145718399fSFrançois Tigeot obj = drm_mode_object_find(dev, plane_resp->plane_id, 18155718399fSFrançois Tigeot DRM_MODE_OBJECT_PLANE); 18165718399fSFrançois Tigeot if (!obj) { 1817b5162e19SFrançois Tigeot ret = -ENOENT; 18185718399fSFrançois Tigeot goto out; 18195718399fSFrançois Tigeot } 18205718399fSFrançois Tigeot plane = obj_to_plane(obj); 18215718399fSFrançois Tigeot 18225718399fSFrançois Tigeot if (plane->crtc) 18235718399fSFrançois Tigeot plane_resp->crtc_id = plane->crtc->base.id; 18245718399fSFrançois Tigeot else 18255718399fSFrançois Tigeot plane_resp->crtc_id = 0; 18265718399fSFrançois Tigeot 18275718399fSFrançois Tigeot if (plane->fb) 18285718399fSFrançois Tigeot plane_resp->fb_id = plane->fb->base.id; 18295718399fSFrançois Tigeot else 18305718399fSFrançois Tigeot plane_resp->fb_id = 0; 18315718399fSFrançois Tigeot 18325718399fSFrançois Tigeot plane_resp->plane_id = plane->base.id; 18335718399fSFrançois Tigeot plane_resp->possible_crtcs = plane->possible_crtcs; 183406fede5aSFrançois Tigeot plane_resp->gamma_size = 0; 18355718399fSFrançois Tigeot 18365718399fSFrançois Tigeot /* 18375718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 18385718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 18395718399fSFrançois Tigeot */ 18405718399fSFrançois Tigeot if (plane->format_count && 18415718399fSFrançois Tigeot (plane_resp->count_format_types >= plane->format_count)) { 1842b5162e19SFrançois Tigeot format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; 1843b5162e19SFrançois Tigeot if (copy_to_user(format_ptr, 18445718399fSFrançois Tigeot plane->format_types, 18455718399fSFrançois Tigeot sizeof(uint32_t) * plane->format_count)) { 1846b5162e19SFrançois Tigeot ret = -EFAULT; 18475718399fSFrançois Tigeot goto out; 18485718399fSFrançois Tigeot } 18495718399fSFrançois Tigeot } 18505718399fSFrançois Tigeot plane_resp->count_format_types = plane->format_count; 18515718399fSFrançois Tigeot 18525718399fSFrançois Tigeot out: 18534dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 18545718399fSFrançois Tigeot return ret; 18555718399fSFrançois Tigeot } 18565718399fSFrançois Tigeot 18575718399fSFrançois Tigeot /** 18585718399fSFrançois Tigeot * drm_mode_setplane - set up or tear down an plane 18595718399fSFrançois Tigeot * @dev: DRM device 18605718399fSFrançois Tigeot * @data: ioctl data* 18614dbb207bSFrançois Tigeot * @file_priv: DRM file info 18625718399fSFrançois Tigeot * 18635718399fSFrançois Tigeot * Set plane info, including placement, fb, scaling, and other factors. 18645718399fSFrançois Tigeot * Or pass a NULL fb to disable. 18655718399fSFrançois Tigeot */ 18665718399fSFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data, 18675718399fSFrançois Tigeot struct drm_file *file_priv) 18685718399fSFrançois Tigeot { 18695718399fSFrançois Tigeot struct drm_mode_set_plane *plane_req = data; 18705718399fSFrançois Tigeot struct drm_mode_object *obj; 18715718399fSFrançois Tigeot struct drm_plane *plane; 18725718399fSFrançois Tigeot struct drm_crtc *crtc; 18734dbb207bSFrançois Tigeot struct drm_framebuffer *fb = NULL, *old_fb = NULL; 18745718399fSFrançois Tigeot int ret = 0; 18755718399fSFrançois Tigeot unsigned int fb_width, fb_height; 18765718399fSFrançois Tigeot int i; 18775718399fSFrançois Tigeot 18785718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1879b5162e19SFrançois Tigeot return -EINVAL; 18805718399fSFrançois Tigeot 18815718399fSFrançois Tigeot /* 18825718399fSFrançois Tigeot * First, find the plane, crtc, and fb objects. If not available, 18835718399fSFrançois Tigeot * we don't bother to call the driver. 18845718399fSFrançois Tigeot */ 18855718399fSFrançois Tigeot obj = drm_mode_object_find(dev, plane_req->plane_id, 18865718399fSFrançois Tigeot DRM_MODE_OBJECT_PLANE); 18875718399fSFrançois Tigeot if (!obj) { 18885718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown plane ID %d\n", 18895718399fSFrançois Tigeot plane_req->plane_id); 18904dbb207bSFrançois Tigeot return -ENOENT; 18915718399fSFrançois Tigeot } 18925718399fSFrançois Tigeot plane = obj_to_plane(obj); 18935718399fSFrançois Tigeot 18945718399fSFrançois Tigeot /* No fb means shut it down */ 18955718399fSFrançois Tigeot if (!plane_req->fb_id) { 18964dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 18974dbb207bSFrançois Tigeot old_fb = plane->fb; 18985718399fSFrançois Tigeot plane->funcs->disable_plane(plane); 18995718399fSFrançois Tigeot plane->crtc = NULL; 19005718399fSFrançois Tigeot plane->fb = NULL; 19014dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 19025718399fSFrançois Tigeot goto out; 19035718399fSFrançois Tigeot } 19045718399fSFrançois Tigeot 19055718399fSFrançois Tigeot obj = drm_mode_object_find(dev, plane_req->crtc_id, 19065718399fSFrançois Tigeot DRM_MODE_OBJECT_CRTC); 19075718399fSFrançois Tigeot if (!obj) { 19085718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown crtc ID %d\n", 19095718399fSFrançois Tigeot plane_req->crtc_id); 1910b5162e19SFrançois Tigeot ret = -ENOENT; 19115718399fSFrançois Tigeot goto out; 19125718399fSFrançois Tigeot } 19135718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 19145718399fSFrançois Tigeot 19154dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, plane_req->fb_id); 19164dbb207bSFrançois Tigeot if (!fb) { 19175718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 19185718399fSFrançois Tigeot plane_req->fb_id); 1919b5162e19SFrançois Tigeot ret = -ENOENT; 19205718399fSFrançois Tigeot goto out; 19215718399fSFrançois Tigeot } 19225718399fSFrançois Tigeot 19235718399fSFrançois Tigeot /* Check whether this plane supports the fb pixel format. */ 19245718399fSFrançois Tigeot for (i = 0; i < plane->format_count; i++) 19255718399fSFrançois Tigeot if (fb->pixel_format == plane->format_types[i]) 19265718399fSFrançois Tigeot break; 19275718399fSFrançois Tigeot if (i == plane->format_count) { 192806fede5aSFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n", 192906fede5aSFrançois Tigeot drm_get_format_name(fb->pixel_format)); 1930b5162e19SFrançois Tigeot ret = -EINVAL; 19315718399fSFrançois Tigeot goto out; 19325718399fSFrançois Tigeot } 19335718399fSFrançois Tigeot 19345718399fSFrançois Tigeot fb_width = fb->width << 16; 19355718399fSFrançois Tigeot fb_height = fb->height << 16; 19365718399fSFrançois Tigeot 19375718399fSFrançois Tigeot /* Make sure source coordinates are inside the fb. */ 19385718399fSFrançois Tigeot if (plane_req->src_w > fb_width || 19395718399fSFrançois Tigeot plane_req->src_x > fb_width - plane_req->src_w || 19405718399fSFrançois Tigeot plane_req->src_h > fb_height || 19415718399fSFrançois Tigeot plane_req->src_y > fb_height - plane_req->src_h) { 19425718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid source coordinates " 19435718399fSFrançois Tigeot "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 19445718399fSFrançois Tigeot plane_req->src_w >> 16, 19455718399fSFrançois Tigeot ((plane_req->src_w & 0xffff) * 15625) >> 10, 19465718399fSFrançois Tigeot plane_req->src_h >> 16, 19475718399fSFrançois Tigeot ((plane_req->src_h & 0xffff) * 15625) >> 10, 19485718399fSFrançois Tigeot plane_req->src_x >> 16, 19495718399fSFrançois Tigeot ((plane_req->src_x & 0xffff) * 15625) >> 10, 19505718399fSFrançois Tigeot plane_req->src_y >> 16, 19515718399fSFrançois Tigeot ((plane_req->src_y & 0xffff) * 15625) >> 10); 1952b5162e19SFrançois Tigeot ret = -ENOSPC; 19535718399fSFrançois Tigeot goto out; 19545718399fSFrançois Tigeot } 19555718399fSFrançois Tigeot 19565718399fSFrançois Tigeot /* Give drivers some help against integer overflows */ 19575718399fSFrançois Tigeot if (plane_req->crtc_w > INT_MAX || 19585718399fSFrançois Tigeot plane_req->crtc_x > INT_MAX - (int32_t) plane_req->crtc_w || 19595718399fSFrançois Tigeot plane_req->crtc_h > INT_MAX || 19605718399fSFrançois Tigeot plane_req->crtc_y > INT_MAX - (int32_t) plane_req->crtc_h) { 19615718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 19625718399fSFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 19635718399fSFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y); 1964b5162e19SFrançois Tigeot ret = -ERANGE; 19655718399fSFrançois Tigeot goto out; 19665718399fSFrançois Tigeot } 19675718399fSFrançois Tigeot 19684dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 1969b5162e19SFrançois Tigeot ret = plane->funcs->update_plane(plane, crtc, fb, 19705718399fSFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y, 19715718399fSFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 19725718399fSFrançois Tigeot plane_req->src_x, plane_req->src_y, 19735718399fSFrançois Tigeot plane_req->src_w, plane_req->src_h); 19745718399fSFrançois Tigeot if (!ret) { 19754dbb207bSFrançois Tigeot old_fb = plane->fb; 19765718399fSFrançois Tigeot plane->crtc = crtc; 19775718399fSFrançois Tigeot plane->fb = fb; 19784dbb207bSFrançois Tigeot fb = NULL; 19795718399fSFrançois Tigeot } 19804dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 19815718399fSFrançois Tigeot 19825718399fSFrançois Tigeot out: 19834dbb207bSFrançois Tigeot if (fb) 19844dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 19854dbb207bSFrançois Tigeot if (old_fb) 19864dbb207bSFrançois Tigeot drm_framebuffer_unreference(old_fb); 19875718399fSFrançois Tigeot 19885718399fSFrançois Tigeot return ret; 19895718399fSFrançois Tigeot } 19905718399fSFrançois Tigeot 19915718399fSFrançois Tigeot /** 19924dbb207bSFrançois Tigeot * drm_mode_set_config_internal - helper to call ->set_config 19934dbb207bSFrançois Tigeot * @set: modeset config to set 19945718399fSFrançois Tigeot * 19954dbb207bSFrançois Tigeot * This is a little helper to wrap internal calls to the ->set_config driver 19964dbb207bSFrançois Tigeot * interface. The only thing it adds is correct refcounting dance. 19974dbb207bSFrançois Tigeot */ 19984dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set) 19994dbb207bSFrançois Tigeot { 20004dbb207bSFrançois Tigeot struct drm_crtc *crtc = set->crtc; 200106fede5aSFrançois Tigeot struct drm_framebuffer *fb; 200206fede5aSFrançois Tigeot struct drm_crtc *tmp; 20034dbb207bSFrançois Tigeot int ret; 20044dbb207bSFrançois Tigeot 200506fede5aSFrançois Tigeot /* 200606fede5aSFrançois Tigeot * NOTE: ->set_config can also disable other crtcs (if we steal all 200706fede5aSFrançois Tigeot * connectors from it), hence we need to refcount the fbs across all 200806fede5aSFrançois Tigeot * crtcs. Atomic modeset will have saner semantics ... 200906fede5aSFrançois Tigeot */ 201006fede5aSFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) 201106fede5aSFrançois Tigeot tmp->old_fb = tmp->fb; 201206fede5aSFrançois Tigeot 20134dbb207bSFrançois Tigeot fb = set->fb; 20144dbb207bSFrançois Tigeot 20154dbb207bSFrançois Tigeot ret = crtc->funcs->set_config(set); 20164dbb207bSFrançois Tigeot if (ret == 0) { 201706fede5aSFrançois Tigeot /* crtc->fb must be updated by ->set_config, enforces this. */ 201806fede5aSFrançois Tigeot WARN_ON(fb != crtc->fb); 201906fede5aSFrançois Tigeot } 202006fede5aSFrançois Tigeot 202106fede5aSFrançois Tigeot list_for_each_entry(tmp, &crtc->dev->mode_config.crtc_list, head) { 202206fede5aSFrançois Tigeot if (tmp->fb) 202306fede5aSFrançois Tigeot drm_framebuffer_reference(tmp->fb); 202406fede5aSFrançois Tigeot if (tmp->old_fb) 202506fede5aSFrançois Tigeot drm_framebuffer_unreference(tmp->old_fb); 20264dbb207bSFrançois Tigeot } 20274dbb207bSFrançois Tigeot 20284dbb207bSFrançois Tigeot return ret; 20294dbb207bSFrançois Tigeot } 20304dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal); 20314dbb207bSFrançois Tigeot 2032*9edbd4a0SFrançois Tigeot /* 2033*9edbd4a0SFrançois Tigeot * Checks that the framebuffer is big enough for the CRTC viewport 2034*9edbd4a0SFrançois Tigeot * (x, y, hdisplay, vdisplay) 2035*9edbd4a0SFrançois Tigeot */ 2036*9edbd4a0SFrançois Tigeot static int drm_crtc_check_viewport(const struct drm_crtc *crtc, 2037*9edbd4a0SFrançois Tigeot int x, int y, 2038*9edbd4a0SFrançois Tigeot const struct drm_display_mode *mode, 2039*9edbd4a0SFrançois Tigeot const struct drm_framebuffer *fb) 2040*9edbd4a0SFrançois Tigeot 2041*9edbd4a0SFrançois Tigeot { 2042*9edbd4a0SFrançois Tigeot int hdisplay, vdisplay; 2043*9edbd4a0SFrançois Tigeot 2044*9edbd4a0SFrançois Tigeot hdisplay = mode->hdisplay; 2045*9edbd4a0SFrançois Tigeot vdisplay = mode->vdisplay; 2046*9edbd4a0SFrançois Tigeot 2047*9edbd4a0SFrançois Tigeot if (drm_mode_is_stereo(mode)) { 2048*9edbd4a0SFrançois Tigeot struct drm_display_mode adjusted = *mode; 2049*9edbd4a0SFrançois Tigeot 2050*9edbd4a0SFrançois Tigeot drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE); 2051*9edbd4a0SFrançois Tigeot hdisplay = adjusted.crtc_hdisplay; 2052*9edbd4a0SFrançois Tigeot vdisplay = adjusted.crtc_vdisplay; 2053*9edbd4a0SFrançois Tigeot } 2054*9edbd4a0SFrançois Tigeot 2055*9edbd4a0SFrançois Tigeot if (crtc->invert_dimensions) 2056*9edbd4a0SFrançois Tigeot swap(hdisplay, vdisplay); 2057*9edbd4a0SFrançois Tigeot 2058*9edbd4a0SFrançois Tigeot if (hdisplay > fb->width || 2059*9edbd4a0SFrançois Tigeot vdisplay > fb->height || 2060*9edbd4a0SFrançois Tigeot x > fb->width - hdisplay || 2061*9edbd4a0SFrançois Tigeot y > fb->height - vdisplay) { 2062*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("Invalid fb size %ux%u for CRTC viewport %ux%u+%d+%d%s.\n", 2063*9edbd4a0SFrançois Tigeot fb->width, fb->height, hdisplay, vdisplay, x, y, 2064*9edbd4a0SFrançois Tigeot crtc->invert_dimensions ? " (inverted)" : ""); 2065*9edbd4a0SFrançois Tigeot return -ENOSPC; 2066*9edbd4a0SFrançois Tigeot } 2067*9edbd4a0SFrançois Tigeot 2068*9edbd4a0SFrançois Tigeot return 0; 2069*9edbd4a0SFrançois Tigeot } 2070*9edbd4a0SFrançois Tigeot 20714dbb207bSFrançois Tigeot /** 20724dbb207bSFrançois Tigeot * drm_mode_setcrtc - set CRTC configuration 20734dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 20744dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 20754dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 20765718399fSFrançois Tigeot * 20775718399fSFrançois Tigeot * Build a new CRTC configuration based on user request. 20785718399fSFrançois Tigeot * 20795718399fSFrançois Tigeot * Called by the user via ioctl. 20805718399fSFrançois Tigeot * 20815718399fSFrançois Tigeot * RETURNS: 20825718399fSFrançois Tigeot * Zero on success, errno on failure. 20835718399fSFrançois Tigeot */ 20845718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data, 20855718399fSFrançois Tigeot struct drm_file *file_priv) 20865718399fSFrançois Tigeot { 20875718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 20885718399fSFrançois Tigeot struct drm_mode_crtc *crtc_req = data; 20895718399fSFrançois Tigeot struct drm_mode_object *obj; 20905718399fSFrançois Tigeot struct drm_crtc *crtc; 20915718399fSFrançois Tigeot struct drm_connector **connector_set = NULL, *connector; 20925718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 20935718399fSFrançois Tigeot struct drm_display_mode *mode = NULL; 20945718399fSFrançois Tigeot struct drm_mode_set set; 2095b5162e19SFrançois Tigeot uint32_t __user *set_connectors_ptr; 2096b5162e19SFrançois Tigeot int ret; 20975718399fSFrançois Tigeot int i; 20985718399fSFrançois Tigeot 20995718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2100b5162e19SFrançois Tigeot return -EINVAL; 21015718399fSFrançois Tigeot 21025718399fSFrançois Tigeot /* For some reason crtc x/y offsets are signed internally. */ 21035718399fSFrançois Tigeot if (crtc_req->x > INT_MAX || crtc_req->y > INT_MAX) 2104b5162e19SFrançois Tigeot return -ERANGE; 21055718399fSFrançois Tigeot 21064dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 21075718399fSFrançois Tigeot obj = drm_mode_object_find(dev, crtc_req->crtc_id, 21085718399fSFrançois Tigeot DRM_MODE_OBJECT_CRTC); 21095718399fSFrançois Tigeot if (!obj) { 21105718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 2111*9edbd4a0SFrançois Tigeot ret = -ENOENT; 21125718399fSFrançois Tigeot goto out; 21135718399fSFrançois Tigeot } 21145718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 21155718399fSFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id); 21165718399fSFrançois Tigeot 21175718399fSFrançois Tigeot if (crtc_req->mode_valid) { 21185718399fSFrançois Tigeot /* If we have a mode we need a framebuffer. */ 21195718399fSFrançois Tigeot /* If we pass -1, set the mode with the currently bound fb */ 21205718399fSFrançois Tigeot if (crtc_req->fb_id == -1) { 21215718399fSFrançois Tigeot if (!crtc->fb) { 21225718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 21235718399fSFrançois Tigeot ret = -EINVAL; 21245718399fSFrançois Tigeot goto out; 21255718399fSFrançois Tigeot } 21265718399fSFrançois Tigeot fb = crtc->fb; 21274dbb207bSFrançois Tigeot /* Make refcounting symmetric with the lookup path. */ 21284dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 21295718399fSFrançois Tigeot } else { 21304dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); 21314dbb207bSFrançois Tigeot if (!fb) { 21325718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown FB ID%d\n", 21335718399fSFrançois Tigeot crtc_req->fb_id); 2134*9edbd4a0SFrançois Tigeot ret = -ENOENT; 21355718399fSFrançois Tigeot goto out; 21365718399fSFrançois Tigeot } 21375718399fSFrançois Tigeot } 21385718399fSFrançois Tigeot 21395718399fSFrançois Tigeot mode = drm_mode_create(dev); 21405718399fSFrançois Tigeot if (!mode) { 2141b5162e19SFrançois Tigeot ret = -ENOMEM; 21425718399fSFrançois Tigeot goto out; 21435718399fSFrançois Tigeot } 21445718399fSFrançois Tigeot 21455718399fSFrançois Tigeot ret = drm_crtc_convert_umode(mode, &crtc_req->mode); 21465718399fSFrançois Tigeot if (ret) { 21475718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid mode\n"); 21485718399fSFrançois Tigeot goto out; 21495718399fSFrançois Tigeot } 21505718399fSFrançois Tigeot 21515718399fSFrançois Tigeot drm_mode_set_crtcinfo(mode, CRTC_INTERLACE_HALVE_V); 21525718399fSFrançois Tigeot 2153*9edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, 2154*9edbd4a0SFrançois Tigeot mode, fb); 2155*9edbd4a0SFrançois Tigeot if (ret) 21565718399fSFrançois Tigeot goto out; 2157*9edbd4a0SFrançois Tigeot 21585718399fSFrançois Tigeot } 21595718399fSFrançois Tigeot 21605718399fSFrançois Tigeot if (crtc_req->count_connectors == 0 && mode) { 21615718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 2162b5162e19SFrançois Tigeot ret = -EINVAL; 21635718399fSFrançois Tigeot goto out; 21645718399fSFrançois Tigeot } 21655718399fSFrançois Tigeot 21665718399fSFrançois Tigeot if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 21675718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 21685718399fSFrançois Tigeot crtc_req->count_connectors); 2169b5162e19SFrançois Tigeot ret = -EINVAL; 21705718399fSFrançois Tigeot goto out; 21715718399fSFrançois Tigeot } 21725718399fSFrançois Tigeot 21735718399fSFrançois Tigeot if (crtc_req->count_connectors > 0) { 21745718399fSFrançois Tigeot u32 out_id; 21755718399fSFrançois Tigeot 21765718399fSFrançois Tigeot /* Avoid unbounded kernel memory allocation */ 21775718399fSFrançois Tigeot if (crtc_req->count_connectors > config->num_connector) { 2178b5162e19SFrançois Tigeot ret = -EINVAL; 21795718399fSFrançois Tigeot goto out; 21805718399fSFrançois Tigeot } 21815718399fSFrançois Tigeot 21825718399fSFrançois Tigeot connector_set = kmalloc(crtc_req->count_connectors * 21834dbb207bSFrançois Tigeot sizeof(struct drm_connector *), 21844dbb207bSFrançois Tigeot M_DRM, M_WAITOK); 2185b5162e19SFrançois Tigeot if (!connector_set) { 2186b5162e19SFrançois Tigeot ret = -ENOMEM; 2187b5162e19SFrançois Tigeot goto out; 2188b5162e19SFrançois Tigeot } 21895718399fSFrançois Tigeot 21905718399fSFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) { 2191b5162e19SFrançois Tigeot set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; 2192b5162e19SFrançois Tigeot if (get_user(out_id, &set_connectors_ptr[i])) { 2193b5162e19SFrançois Tigeot ret = -EFAULT; 21945718399fSFrançois Tigeot goto out; 21955718399fSFrançois Tigeot } 21965718399fSFrançois Tigeot 21975718399fSFrançois Tigeot obj = drm_mode_object_find(dev, out_id, 21985718399fSFrançois Tigeot DRM_MODE_OBJECT_CONNECTOR); 21995718399fSFrançois Tigeot if (!obj) { 22005718399fSFrançois Tigeot DRM_DEBUG_KMS("Connector id %d unknown\n", 22015718399fSFrançois Tigeot out_id); 2202*9edbd4a0SFrançois Tigeot ret = -ENOENT; 22035718399fSFrançois Tigeot goto out; 22045718399fSFrançois Tigeot } 22055718399fSFrançois Tigeot connector = obj_to_connector(obj); 22065718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 22075718399fSFrançois Tigeot connector->base.id, 22085718399fSFrançois Tigeot drm_get_connector_name(connector)); 22095718399fSFrançois Tigeot 22105718399fSFrançois Tigeot connector_set[i] = connector; 22115718399fSFrançois Tigeot } 22125718399fSFrançois Tigeot } 22135718399fSFrançois Tigeot 22145718399fSFrançois Tigeot set.crtc = crtc; 22155718399fSFrançois Tigeot set.x = crtc_req->x; 22165718399fSFrançois Tigeot set.y = crtc_req->y; 22175718399fSFrançois Tigeot set.mode = mode; 22185718399fSFrançois Tigeot set.connectors = connector_set; 22195718399fSFrançois Tigeot set.num_connectors = crtc_req->count_connectors; 22205718399fSFrançois Tigeot set.fb = fb; 22214dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 22225718399fSFrançois Tigeot 22235718399fSFrançois Tigeot out: 22244dbb207bSFrançois Tigeot if (fb) 22254dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 22264dbb207bSFrançois Tigeot 22274dbb207bSFrançois Tigeot kfree(connector_set); 22285718399fSFrançois Tigeot drm_mode_destroy(dev, mode); 22294dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 22305718399fSFrançois Tigeot return ret; 22315718399fSFrançois Tigeot } 22325718399fSFrançois Tigeot 223306fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev, 223406fede5aSFrançois Tigeot struct drm_mode_cursor2 *req, 223506fede5aSFrançois Tigeot struct drm_file *file_priv) 22365718399fSFrançois Tigeot { 22375718399fSFrançois Tigeot struct drm_mode_object *obj; 22385718399fSFrançois Tigeot struct drm_crtc *crtc; 22395718399fSFrançois Tigeot int ret = 0; 22405718399fSFrançois Tigeot 22415718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2242b5162e19SFrançois Tigeot return -EINVAL; 22435718399fSFrançois Tigeot 2244b5162e19SFrançois Tigeot if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) 2245b5162e19SFrançois Tigeot return -EINVAL; 22465718399fSFrançois Tigeot 22475718399fSFrançois Tigeot obj = drm_mode_object_find(dev, req->crtc_id, DRM_MODE_OBJECT_CRTC); 22485718399fSFrançois Tigeot if (!obj) { 22495718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 2250*9edbd4a0SFrançois Tigeot return -ENOENT; 22515718399fSFrançois Tigeot } 22525718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 22535718399fSFrançois Tigeot 22544dbb207bSFrançois Tigeot mutex_lock(&crtc->mutex); 22555718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 225606fede5aSFrançois Tigeot if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { 2257b5162e19SFrançois Tigeot ret = -ENXIO; 22585718399fSFrançois Tigeot goto out; 22595718399fSFrançois Tigeot } 22605718399fSFrançois Tigeot /* Turns off the cursor if handle is 0 */ 226106fede5aSFrançois Tigeot if (crtc->funcs->cursor_set2) 226206fede5aSFrançois Tigeot ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, 226306fede5aSFrançois Tigeot req->width, req->height, req->hot_x, req->hot_y); 226406fede5aSFrançois Tigeot else 2265b5162e19SFrançois Tigeot ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, 22665718399fSFrançois Tigeot req->width, req->height); 22675718399fSFrançois Tigeot } 22685718399fSFrançois Tigeot 22695718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 22705718399fSFrançois Tigeot if (crtc->funcs->cursor_move) { 22715718399fSFrançois Tigeot ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 22725718399fSFrançois Tigeot } else { 2273b5162e19SFrançois Tigeot ret = -EFAULT; 22745718399fSFrançois Tigeot goto out; 22755718399fSFrançois Tigeot } 22765718399fSFrançois Tigeot } 22775718399fSFrançois Tigeot out: 22784dbb207bSFrançois Tigeot mutex_unlock(&crtc->mutex); 22794dbb207bSFrançois Tigeot 22805718399fSFrançois Tigeot return ret; 228106fede5aSFrançois Tigeot 228206fede5aSFrançois Tigeot } 228306fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev, 228406fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 228506fede5aSFrançois Tigeot { 228606fede5aSFrançois Tigeot struct drm_mode_cursor *req = data; 228706fede5aSFrançois Tigeot struct drm_mode_cursor2 new_req; 228806fede5aSFrançois Tigeot 228906fede5aSFrançois Tigeot memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); 229006fede5aSFrançois Tigeot new_req.hot_x = new_req.hot_y = 0; 229106fede5aSFrançois Tigeot 229206fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, &new_req, file_priv); 229306fede5aSFrançois Tigeot } 229406fede5aSFrançois Tigeot 229506fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev, 229606fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 229706fede5aSFrançois Tigeot { 229806fede5aSFrançois Tigeot struct drm_mode_cursor2 *req = data; 229906fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, req, file_priv); 23005718399fSFrançois Tigeot } 23015718399fSFrançois Tigeot 23025718399fSFrançois Tigeot /* Original addfb only supported RGB formats, so figure out which one */ 23035718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) 23045718399fSFrançois Tigeot { 23055718399fSFrançois Tigeot uint32_t fmt; 23065718399fSFrançois Tigeot 23075718399fSFrançois Tigeot switch (bpp) { 23085718399fSFrançois Tigeot case 8: 23094dbb207bSFrançois Tigeot fmt = DRM_FORMAT_C8; 23105718399fSFrançois Tigeot break; 23115718399fSFrançois Tigeot case 16: 23125718399fSFrançois Tigeot if (depth == 15) 23135718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB1555; 23145718399fSFrançois Tigeot else 23155718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB565; 23165718399fSFrançois Tigeot break; 23175718399fSFrançois Tigeot case 24: 23185718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB888; 23195718399fSFrançois Tigeot break; 23205718399fSFrançois Tigeot case 32: 23215718399fSFrançois Tigeot if (depth == 24) 23225718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 23235718399fSFrançois Tigeot else if (depth == 30) 23245718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB2101010; 23255718399fSFrançois Tigeot else 23265718399fSFrançois Tigeot fmt = DRM_FORMAT_ARGB8888; 23275718399fSFrançois Tigeot break; 23285718399fSFrançois Tigeot default: 2329b5162e19SFrançois Tigeot DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); 23305718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 23315718399fSFrançois Tigeot break; 23325718399fSFrançois Tigeot } 23335718399fSFrançois Tigeot 23345718399fSFrançois Tigeot return fmt; 23355718399fSFrançois Tigeot } 2336b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format); 23375718399fSFrançois Tigeot 23385718399fSFrançois Tigeot /** 23395718399fSFrançois Tigeot * drm_mode_addfb - add an FB to the graphics configuration 23404dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 23414dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 23424dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 23435718399fSFrançois Tigeot * 23445718399fSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request. 23455718399fSFrançois Tigeot * 23465718399fSFrançois Tigeot * Called by the user via ioctl. 23475718399fSFrançois Tigeot * 23485718399fSFrançois Tigeot * RETURNS: 23495718399fSFrançois Tigeot * Zero on success, errno on failure. 23505718399fSFrançois Tigeot */ 23515718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev, 23525718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 23535718399fSFrançois Tigeot { 23545718399fSFrançois Tigeot struct drm_mode_fb_cmd *or = data; 23555718399fSFrançois Tigeot struct drm_mode_fb_cmd2 r = {}; 23565718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 23575718399fSFrançois Tigeot struct drm_framebuffer *fb; 23585718399fSFrançois Tigeot int ret = 0; 23595718399fSFrançois Tigeot 23605718399fSFrançois Tigeot /* Use new struct with format internally */ 23615718399fSFrançois Tigeot r.fb_id = or->fb_id; 23625718399fSFrançois Tigeot r.width = or->width; 23635718399fSFrançois Tigeot r.height = or->height; 23645718399fSFrançois Tigeot r.pitches[0] = or->pitch; 23655718399fSFrançois Tigeot r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 23665718399fSFrançois Tigeot r.handles[0] = or->handle; 23675718399fSFrançois Tigeot 23685718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2369b5162e19SFrançois Tigeot return -EINVAL; 23705718399fSFrançois Tigeot 23715718399fSFrançois Tigeot if ((config->min_width > r.width) || (r.width > config->max_width)) 2372b5162e19SFrançois Tigeot return -EINVAL; 2373b5162e19SFrançois Tigeot 23745718399fSFrançois Tigeot if ((config->min_height > r.height) || (r.height > config->max_height)) 2375b5162e19SFrançois Tigeot return -EINVAL; 23765718399fSFrançois Tigeot 237760fc7eecSFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, &r); 237860fc7eecSFrançois Tigeot if (IS_ERR(fb)) { 237960fc7eecSFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 23804dbb207bSFrançois Tigeot return PTR_ERR(fb); 23815718399fSFrançois Tigeot } 23825718399fSFrançois Tigeot 23834dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 23845718399fSFrançois Tigeot or->fb_id = fb->base.id; 23855718399fSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 23865718399fSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 23874dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 23885718399fSFrançois Tigeot 23895718399fSFrançois Tigeot return ret; 23905718399fSFrançois Tigeot } 23915718399fSFrançois Tigeot 2392b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r) 23935718399fSFrançois Tigeot { 23945718399fSFrançois Tigeot uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 23955718399fSFrançois Tigeot 23965718399fSFrançois Tigeot switch (format) { 23975718399fSFrançois Tigeot case DRM_FORMAT_C8: 23985718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 23995718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 24005718399fSFrançois Tigeot case DRM_FORMAT_XRGB4444: 24015718399fSFrançois Tigeot case DRM_FORMAT_XBGR4444: 24025718399fSFrançois Tigeot case DRM_FORMAT_RGBX4444: 24035718399fSFrançois Tigeot case DRM_FORMAT_BGRX4444: 24045718399fSFrançois Tigeot case DRM_FORMAT_ARGB4444: 24055718399fSFrançois Tigeot case DRM_FORMAT_ABGR4444: 24065718399fSFrançois Tigeot case DRM_FORMAT_RGBA4444: 24075718399fSFrançois Tigeot case DRM_FORMAT_BGRA4444: 24085718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 24095718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 24105718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 24115718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 24125718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 24135718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 24145718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 24155718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 24165718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 24175718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 24185718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 24195718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 24205718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 24215718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 24225718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 24235718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 24245718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 24255718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 24265718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 24275718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 24285718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 24295718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 24305718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 24315718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 24325718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 24335718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 24345718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 24355718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 24365718399fSFrançois Tigeot case DRM_FORMAT_YUYV: 24375718399fSFrançois Tigeot case DRM_FORMAT_YVYU: 24385718399fSFrançois Tigeot case DRM_FORMAT_UYVY: 24395718399fSFrançois Tigeot case DRM_FORMAT_VYUY: 24405718399fSFrançois Tigeot case DRM_FORMAT_AYUV: 24415718399fSFrançois Tigeot case DRM_FORMAT_NV12: 24425718399fSFrançois Tigeot case DRM_FORMAT_NV21: 24435718399fSFrançois Tigeot case DRM_FORMAT_NV16: 24445718399fSFrançois Tigeot case DRM_FORMAT_NV61: 2445b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 2446b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 24475718399fSFrançois Tigeot case DRM_FORMAT_YUV410: 24485718399fSFrançois Tigeot case DRM_FORMAT_YVU410: 24495718399fSFrançois Tigeot case DRM_FORMAT_YUV411: 24505718399fSFrançois Tigeot case DRM_FORMAT_YVU411: 24515718399fSFrançois Tigeot case DRM_FORMAT_YUV420: 24525718399fSFrançois Tigeot case DRM_FORMAT_YVU420: 24535718399fSFrançois Tigeot case DRM_FORMAT_YUV422: 24545718399fSFrançois Tigeot case DRM_FORMAT_YVU422: 24555718399fSFrançois Tigeot case DRM_FORMAT_YUV444: 24565718399fSFrançois Tigeot case DRM_FORMAT_YVU444: 24575718399fSFrançois Tigeot return 0; 24585718399fSFrançois Tigeot default: 2459*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("invalid pixel format %s\n", 2460*9edbd4a0SFrançois Tigeot drm_get_format_name(r->pixel_format)); 2461b5162e19SFrançois Tigeot return -EINVAL; 24625718399fSFrançois Tigeot } 24635718399fSFrançois Tigeot } 24645718399fSFrançois Tigeot 2465b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) 2466b5162e19SFrançois Tigeot { 2467b5162e19SFrançois Tigeot int ret, hsub, vsub, num_planes, i; 2468b5162e19SFrançois Tigeot 2469b5162e19SFrançois Tigeot ret = format_check(r); 2470b5162e19SFrançois Tigeot if (ret) { 247106fede5aSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer format %s\n", 247206fede5aSFrançois Tigeot drm_get_format_name(r->pixel_format)); 2473b5162e19SFrançois Tigeot return ret; 2474b5162e19SFrançois Tigeot } 2475b5162e19SFrançois Tigeot 2476b5162e19SFrançois Tigeot hsub = drm_format_horz_chroma_subsampling(r->pixel_format); 2477b5162e19SFrançois Tigeot vsub = drm_format_vert_chroma_subsampling(r->pixel_format); 2478b5162e19SFrançois Tigeot num_planes = drm_format_num_planes(r->pixel_format); 2479b5162e19SFrançois Tigeot 2480b5162e19SFrançois Tigeot if (r->width == 0 || r->width % hsub) { 2481b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %u\n", r->height); 2482b5162e19SFrançois Tigeot return -EINVAL; 2483b5162e19SFrançois Tigeot } 2484b5162e19SFrançois Tigeot 2485b5162e19SFrançois Tigeot if (r->height == 0 || r->height % vsub) { 2486b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); 2487b5162e19SFrançois Tigeot return -EINVAL; 2488b5162e19SFrançois Tigeot } 2489b5162e19SFrançois Tigeot 2490b5162e19SFrançois Tigeot for (i = 0; i < num_planes; i++) { 2491b5162e19SFrançois Tigeot unsigned int width = r->width / (i != 0 ? hsub : 1); 2492b5162e19SFrançois Tigeot unsigned int height = r->height / (i != 0 ? vsub : 1); 2493b5162e19SFrançois Tigeot unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); 2494b5162e19SFrançois Tigeot 2495b5162e19SFrançois Tigeot if (!r->handles[i]) { 2496b5162e19SFrançois Tigeot DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); 2497b5162e19SFrançois Tigeot return -EINVAL; 2498b5162e19SFrançois Tigeot } 2499b5162e19SFrançois Tigeot 2500b5162e19SFrançois Tigeot if ((uint64_t) width * cpp > UINT_MAX) 2501b5162e19SFrançois Tigeot return -ERANGE; 2502b5162e19SFrançois Tigeot 2503b5162e19SFrançois Tigeot if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 2504b5162e19SFrançois Tigeot return -ERANGE; 2505b5162e19SFrançois Tigeot 2506b5162e19SFrançois Tigeot if (r->pitches[i] < width * cpp) { 2507b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); 2508b5162e19SFrançois Tigeot return -EINVAL; 2509b5162e19SFrançois Tigeot } 2510b5162e19SFrançois Tigeot } 2511b5162e19SFrançois Tigeot 2512b5162e19SFrançois Tigeot return 0; 2513b5162e19SFrançois Tigeot } 2514b5162e19SFrançois Tigeot 25155718399fSFrançois Tigeot /** 25165718399fSFrançois Tigeot * drm_mode_addfb2 - add an FB to the graphics configuration 25174dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 25184dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 25194dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 25205718399fSFrançois Tigeot * 25215718399fSFrançois Tigeot * Add a new FB to the specified CRTC, given a user request with format. 25225718399fSFrançois Tigeot * 25235718399fSFrançois Tigeot * Called by the user via ioctl. 25245718399fSFrançois Tigeot * 25255718399fSFrançois Tigeot * RETURNS: 25265718399fSFrançois Tigeot * Zero on success, errno on failure. 25275718399fSFrançois Tigeot */ 25285718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev, 25295718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 25305718399fSFrançois Tigeot { 25315718399fSFrançois Tigeot struct drm_mode_fb_cmd2 *r = data; 25325718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 25335718399fSFrançois Tigeot struct drm_framebuffer *fb; 2534b5162e19SFrançois Tigeot int ret; 25355718399fSFrançois Tigeot 25365718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2537b5162e19SFrançois Tigeot return -EINVAL; 2538b5162e19SFrançois Tigeot 2539b5162e19SFrançois Tigeot if (r->flags & ~DRM_MODE_FB_INTERLACED) { 2540b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); 2541b5162e19SFrançois Tigeot return -EINVAL; 2542b5162e19SFrançois Tigeot } 25435718399fSFrançois Tigeot 25445718399fSFrançois Tigeot if ((config->min_width > r->width) || (r->width > config->max_width)) { 2545b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", 25465718399fSFrançois Tigeot r->width, config->min_width, config->max_width); 2547b5162e19SFrançois Tigeot return -EINVAL; 25485718399fSFrançois Tigeot } 25495718399fSFrançois Tigeot if ((config->min_height > r->height) || (r->height > config->max_height)) { 2550b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", 25515718399fSFrançois Tigeot r->height, config->min_height, config->max_height); 2552b5162e19SFrançois Tigeot return -EINVAL; 25535718399fSFrançois Tigeot } 25545718399fSFrançois Tigeot 2555b5162e19SFrançois Tigeot ret = framebuffer_check(r); 2556b5162e19SFrançois Tigeot if (ret) 25575718399fSFrançois Tigeot return ret; 25585718399fSFrançois Tigeot 255960fc7eecSFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 256060fc7eecSFrançois Tigeot if (IS_ERR(fb)) { 256160fc7eecSFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 25624dbb207bSFrançois Tigeot return PTR_ERR(fb); 25635718399fSFrançois Tigeot } 25645718399fSFrançois Tigeot 25654dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 25665718399fSFrançois Tigeot r->fb_id = fb->base.id; 25675718399fSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 25685718399fSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 25694dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 25705718399fSFrançois Tigeot 25714dbb207bSFrançois Tigeot 2572b5162e19SFrançois Tigeot return ret; 25735718399fSFrançois Tigeot } 25745718399fSFrançois Tigeot 25755718399fSFrançois Tigeot /** 25765718399fSFrançois Tigeot * drm_mode_rmfb - remove an FB from the configuration 25774dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 25784dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 25794dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 25805718399fSFrançois Tigeot * 25815718399fSFrançois Tigeot * Remove the FB specified by the user. 25825718399fSFrançois Tigeot * 25835718399fSFrançois Tigeot * Called by the user via ioctl. 25845718399fSFrançois Tigeot * 25855718399fSFrançois Tigeot * RETURNS: 25865718399fSFrançois Tigeot * Zero on success, errno on failure. 25875718399fSFrançois Tigeot */ 25885718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev, 25895718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 25905718399fSFrançois Tigeot { 25915718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 25925718399fSFrançois Tigeot struct drm_framebuffer *fbl = NULL; 25935718399fSFrançois Tigeot uint32_t *id = data; 25945718399fSFrançois Tigeot int found = 0; 25955718399fSFrançois Tigeot 25965718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2597b5162e19SFrançois Tigeot return -EINVAL; 25985718399fSFrançois Tigeot 25994dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 26004dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 26014dbb207bSFrançois Tigeot fb = __drm_framebuffer_lookup(dev, *id); 26024dbb207bSFrançois Tigeot if (!fb) 26034dbb207bSFrançois Tigeot goto fail_lookup; 26045718399fSFrançois Tigeot 26055718399fSFrançois Tigeot list_for_each_entry(fbl, &file_priv->fbs, filp_head) 26065718399fSFrançois Tigeot if (fb == fbl) 26075718399fSFrançois Tigeot found = 1; 26084dbb207bSFrançois Tigeot if (!found) 26094dbb207bSFrançois Tigeot goto fail_lookup; 26105718399fSFrançois Tigeot 26114dbb207bSFrançois Tigeot /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ 26124dbb207bSFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 26134dbb207bSFrançois Tigeot 26144dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 26154dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 26164dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 26175718399fSFrançois Tigeot 2618b5162e19SFrançois Tigeot drm_framebuffer_remove(fb); 26195718399fSFrançois Tigeot 26204dbb207bSFrançois Tigeot return 0; 26214dbb207bSFrançois Tigeot 26224dbb207bSFrançois Tigeot fail_lookup: 26234dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 26244dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 26254dbb207bSFrançois Tigeot 2626*9edbd4a0SFrançois Tigeot return -ENOENT; 26275718399fSFrançois Tigeot } 26285718399fSFrançois Tigeot 26295718399fSFrançois Tigeot /** 26305718399fSFrançois Tigeot * drm_mode_getfb - get FB info 26314dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 26324dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 26334dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 26345718399fSFrançois Tigeot * 26355718399fSFrançois Tigeot * Lookup the FB given its ID and return info about it. 26365718399fSFrançois Tigeot * 26375718399fSFrançois Tigeot * Called by the user via ioctl. 26385718399fSFrançois Tigeot * 26395718399fSFrançois Tigeot * RETURNS: 26405718399fSFrançois Tigeot * Zero on success, errno on failure. 26415718399fSFrançois Tigeot */ 26425718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev, 26435718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 26445718399fSFrançois Tigeot { 26455718399fSFrançois Tigeot struct drm_mode_fb_cmd *r = data; 26465718399fSFrançois Tigeot struct drm_framebuffer *fb; 26474dbb207bSFrançois Tigeot int ret; 26485718399fSFrançois Tigeot 26495718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2650b5162e19SFrançois Tigeot return -EINVAL; 26515718399fSFrançois Tigeot 26524dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 26534dbb207bSFrançois Tigeot if (!fb) 2654*9edbd4a0SFrançois Tigeot return -ENOENT; 26555718399fSFrançois Tigeot 26565718399fSFrançois Tigeot r->height = fb->height; 26575718399fSFrançois Tigeot r->width = fb->width; 26585718399fSFrançois Tigeot r->depth = fb->depth; 26595718399fSFrançois Tigeot r->bpp = fb->bits_per_pixel; 26605718399fSFrançois Tigeot r->pitch = fb->pitches[0]; 26614dbb207bSFrançois Tigeot if (fb->funcs->create_handle) 26624dbb207bSFrançois Tigeot ret = fb->funcs->create_handle(fb, file_priv, &r->handle); 26634dbb207bSFrançois Tigeot else 26644dbb207bSFrançois Tigeot ret = -ENODEV; 26655718399fSFrançois Tigeot 26664dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 26674dbb207bSFrançois Tigeot 26685718399fSFrançois Tigeot return ret; 26695718399fSFrançois Tigeot } 26705718399fSFrançois Tigeot 26715718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev, 26725718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 26735718399fSFrançois Tigeot { 26745718399fSFrançois Tigeot struct drm_clip_rect __user *clips_ptr; 26755718399fSFrançois Tigeot struct drm_clip_rect *clips = NULL; 26765718399fSFrançois Tigeot struct drm_mode_fb_dirty_cmd *r = data; 26775718399fSFrançois Tigeot struct drm_framebuffer *fb; 26785718399fSFrançois Tigeot unsigned flags; 26795718399fSFrançois Tigeot int num_clips; 2680b5162e19SFrançois Tigeot int ret; 26815718399fSFrançois Tigeot 26825718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2683b5162e19SFrançois Tigeot return -EINVAL; 26845718399fSFrançois Tigeot 26854dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 26864dbb207bSFrançois Tigeot if (!fb) 2687*9edbd4a0SFrançois Tigeot return -ENOENT; 26885718399fSFrançois Tigeot 26895718399fSFrançois Tigeot num_clips = r->num_clips; 2690b5162e19SFrançois Tigeot clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 26915718399fSFrançois Tigeot 26925718399fSFrançois Tigeot if (!num_clips != !clips_ptr) { 2693b5162e19SFrançois Tigeot ret = -EINVAL; 26945718399fSFrançois Tigeot goto out_err1; 26955718399fSFrançois Tigeot } 26965718399fSFrançois Tigeot 26975718399fSFrançois Tigeot flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 26985718399fSFrançois Tigeot 26995718399fSFrançois Tigeot /* If userspace annotates copy, clips must come in pairs */ 27005718399fSFrançois Tigeot if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 2701b5162e19SFrançois Tigeot ret = -EINVAL; 27025718399fSFrançois Tigeot goto out_err1; 27035718399fSFrançois Tigeot } 27045718399fSFrançois Tigeot 27055718399fSFrançois Tigeot if (num_clips && clips_ptr) { 27065718399fSFrançois Tigeot if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 2707b5162e19SFrançois Tigeot ret = -EINVAL; 27085718399fSFrançois Tigeot goto out_err1; 27095718399fSFrançois Tigeot } 27104dbb207bSFrançois Tigeot clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL); 2711b5162e19SFrançois Tigeot if (!clips) { 2712b5162e19SFrançois Tigeot ret = -ENOMEM; 2713b5162e19SFrançois Tigeot goto out_err1; 2714b5162e19SFrançois Tigeot } 27155718399fSFrançois Tigeot 2716b5162e19SFrançois Tigeot ret = copy_from_user(clips, clips_ptr, 2717b5162e19SFrançois Tigeot num_clips * sizeof(*clips)); 2718b5162e19SFrançois Tigeot if (ret) { 2719b5162e19SFrançois Tigeot ret = -EFAULT; 27205718399fSFrançois Tigeot goto out_err2; 27215718399fSFrançois Tigeot } 2722b5162e19SFrançois Tigeot } 27235718399fSFrançois Tigeot 27245718399fSFrançois Tigeot if (fb->funcs->dirty) { 2725b5162e19SFrançois Tigeot ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 27265718399fSFrançois Tigeot clips, num_clips); 27275718399fSFrançois Tigeot } else { 2728b5162e19SFrançois Tigeot ret = -ENOSYS; 27295718399fSFrançois Tigeot } 27305718399fSFrançois Tigeot 27315718399fSFrançois Tigeot out_err2: 27324dbb207bSFrançois Tigeot kfree(clips); 27335718399fSFrançois Tigeot out_err1: 27344dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 27354dbb207bSFrançois Tigeot 27365718399fSFrançois Tigeot return ret; 27375718399fSFrançois Tigeot } 27385718399fSFrançois Tigeot 27395718399fSFrançois Tigeot 27405718399fSFrançois Tigeot /** 27415718399fSFrançois Tigeot * drm_fb_release - remove and free the FBs on this file 27424dbb207bSFrançois Tigeot * @priv: drm file for the ioctl 27435718399fSFrançois Tigeot * 27445718399fSFrançois Tigeot * Destroy all the FBs associated with @filp. 27455718399fSFrançois Tigeot * 27465718399fSFrançois Tigeot * Called by the user via ioctl. 27475718399fSFrançois Tigeot * 27485718399fSFrançois Tigeot * RETURNS: 27495718399fSFrançois Tigeot * Zero on success, errno on failure. 27505718399fSFrançois Tigeot */ 27515718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv) 27525718399fSFrançois Tigeot { 27534dbb207bSFrançois Tigeot #if 0 27545718399fSFrançois Tigeot struct drm_device *dev = priv->minor->dev; 27554dbb207bSFrançois Tigeot #else 27564dbb207bSFrançois Tigeot struct drm_device *dev = priv->dev; 275779f713b0SFrançois Tigeot #endif 27585718399fSFrançois Tigeot struct drm_framebuffer *fb, *tfb; 27595718399fSFrançois Tigeot 27604dbb207bSFrançois Tigeot mutex_lock(&priv->fbs_lock); 27615718399fSFrançois Tigeot list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 27624dbb207bSFrançois Tigeot 27634dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 27644dbb207bSFrançois Tigeot /* Mark fb as reaped, we still have a ref from fpriv->fbs. */ 27654dbb207bSFrançois Tigeot __drm_framebuffer_unregister(dev, fb); 27664dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 27674dbb207bSFrançois Tigeot 27684dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 27694dbb207bSFrançois Tigeot 27704dbb207bSFrançois Tigeot /* This will also drop the fpriv->fbs reference. */ 2771b5162e19SFrançois Tigeot drm_framebuffer_remove(fb); 27725718399fSFrançois Tigeot } 27734dbb207bSFrançois Tigeot mutex_unlock(&priv->fbs_lock); 27745718399fSFrançois Tigeot } 27755718399fSFrançois Tigeot 27765718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags, 27775718399fSFrançois Tigeot const char *name, int num_values) 27785718399fSFrançois Tigeot { 27795718399fSFrançois Tigeot struct drm_property *property = NULL; 27805718399fSFrançois Tigeot int ret; 27815718399fSFrançois Tigeot 27824dbb207bSFrançois Tigeot property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); 2783b5162e19SFrançois Tigeot if (!property) 2784b5162e19SFrançois Tigeot return NULL; 27855718399fSFrançois Tigeot 27865718399fSFrançois Tigeot if (num_values) { 27874dbb207bSFrançois Tigeot property->values = kzalloc(sizeof(uint64_t)*num_values, GFP_KERNEL); 2788b5162e19SFrançois Tigeot if (!property->values) 2789b5162e19SFrançois Tigeot goto fail; 27905718399fSFrançois Tigeot } 27915718399fSFrançois Tigeot 27925718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); 27935718399fSFrançois Tigeot if (ret) 27945718399fSFrançois Tigeot goto fail; 2795b5162e19SFrançois Tigeot 27965718399fSFrançois Tigeot property->flags = flags; 27975718399fSFrançois Tigeot property->num_values = num_values; 27985718399fSFrançois Tigeot INIT_LIST_HEAD(&property->enum_blob_list); 27995718399fSFrançois Tigeot 28005718399fSFrançois Tigeot if (name) { 28015718399fSFrançois Tigeot strncpy(property->name, name, DRM_PROP_NAME_LEN); 28025718399fSFrançois Tigeot property->name[DRM_PROP_NAME_LEN-1] = '\0'; 28035718399fSFrançois Tigeot } 28045718399fSFrançois Tigeot 28055718399fSFrançois Tigeot list_add_tail(&property->head, &dev->mode_config.property_list); 28065718399fSFrançois Tigeot return property; 28075718399fSFrançois Tigeot fail: 28084dbb207bSFrançois Tigeot kfree(property->values); 28094dbb207bSFrançois Tigeot kfree(property); 2810b5162e19SFrançois Tigeot return NULL; 28115718399fSFrançois Tigeot } 2812b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create); 28135718399fSFrançois Tigeot 28145718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, 28155718399fSFrançois Tigeot const char *name, 28165718399fSFrançois Tigeot const struct drm_prop_enum_list *props, 28175718399fSFrançois Tigeot int num_values) 28185718399fSFrançois Tigeot { 28195718399fSFrançois Tigeot struct drm_property *property; 28205718399fSFrançois Tigeot int i, ret; 28215718399fSFrançois Tigeot 28225718399fSFrançois Tigeot flags |= DRM_MODE_PROP_ENUM; 28235718399fSFrançois Tigeot 28245718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 28255718399fSFrançois Tigeot if (!property) 28265718399fSFrançois Tigeot return NULL; 28275718399fSFrançois Tigeot 28285718399fSFrançois Tigeot for (i = 0; i < num_values; i++) { 28295718399fSFrançois Tigeot ret = drm_property_add_enum(property, i, 28305718399fSFrançois Tigeot props[i].type, 28315718399fSFrançois Tigeot props[i].name); 28325718399fSFrançois Tigeot if (ret) { 28335718399fSFrançois Tigeot drm_property_destroy(dev, property); 28345718399fSFrançois Tigeot return NULL; 28355718399fSFrançois Tigeot } 28365718399fSFrançois Tigeot } 28375718399fSFrançois Tigeot 28385718399fSFrançois Tigeot return property; 28395718399fSFrançois Tigeot } 2840b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum); 2841b5162e19SFrançois Tigeot 2842b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev, 2843b5162e19SFrançois Tigeot int flags, const char *name, 2844b5162e19SFrançois Tigeot const struct drm_prop_enum_list *props, 2845b5162e19SFrançois Tigeot int num_values) 2846b5162e19SFrançois Tigeot { 2847b5162e19SFrançois Tigeot struct drm_property *property; 2848b5162e19SFrançois Tigeot int i, ret; 2849b5162e19SFrançois Tigeot 2850b5162e19SFrançois Tigeot flags |= DRM_MODE_PROP_BITMASK; 2851b5162e19SFrançois Tigeot 2852b5162e19SFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 2853b5162e19SFrançois Tigeot if (!property) 2854b5162e19SFrançois Tigeot return NULL; 2855b5162e19SFrançois Tigeot 2856b5162e19SFrançois Tigeot for (i = 0; i < num_values; i++) { 2857b5162e19SFrançois Tigeot ret = drm_property_add_enum(property, i, 2858b5162e19SFrançois Tigeot props[i].type, 2859b5162e19SFrançois Tigeot props[i].name); 2860b5162e19SFrançois Tigeot if (ret) { 2861b5162e19SFrançois Tigeot drm_property_destroy(dev, property); 2862b5162e19SFrançois Tigeot return NULL; 2863b5162e19SFrançois Tigeot } 2864b5162e19SFrançois Tigeot } 2865b5162e19SFrançois Tigeot 2866b5162e19SFrançois Tigeot return property; 2867b5162e19SFrançois Tigeot } 2868b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask); 28695718399fSFrançois Tigeot 28705718399fSFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, 28715718399fSFrançois Tigeot const char *name, 28725718399fSFrançois Tigeot uint64_t min, uint64_t max) 28735718399fSFrançois Tigeot { 28745718399fSFrançois Tigeot struct drm_property *property; 28755718399fSFrançois Tigeot 28765718399fSFrançois Tigeot flags |= DRM_MODE_PROP_RANGE; 28775718399fSFrançois Tigeot 28785718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, 2); 28795718399fSFrançois Tigeot if (!property) 28805718399fSFrançois Tigeot return NULL; 28815718399fSFrançois Tigeot 28825718399fSFrançois Tigeot property->values[0] = min; 28835718399fSFrançois Tigeot property->values[1] = max; 28845718399fSFrançois Tigeot 28855718399fSFrançois Tigeot return property; 28865718399fSFrançois Tigeot } 2887b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range); 28885718399fSFrançois Tigeot 28895718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index, 28905718399fSFrançois Tigeot uint64_t value, const char *name) 28915718399fSFrançois Tigeot { 28925718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 28935718399fSFrançois Tigeot 2894b5162e19SFrançois Tigeot if (!(property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK))) 2895b5162e19SFrançois Tigeot return -EINVAL; 2896b5162e19SFrançois Tigeot 2897b5162e19SFrançois Tigeot /* 2898b5162e19SFrançois Tigeot * Bitmask enum properties have the additional constraint of values 2899b5162e19SFrançois Tigeot * from 0 to 63 2900b5162e19SFrançois Tigeot */ 2901b5162e19SFrançois Tigeot if ((property->flags & DRM_MODE_PROP_BITMASK) && (value > 63)) 29025718399fSFrançois Tigeot return -EINVAL; 29035718399fSFrançois Tigeot 29045718399fSFrançois Tigeot if (!list_empty(&property->enum_blob_list)) { 29055718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 29065718399fSFrançois Tigeot if (prop_enum->value == value) { 29075718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 29085718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 29095718399fSFrançois Tigeot return 0; 29105718399fSFrançois Tigeot } 29115718399fSFrançois Tigeot } 29125718399fSFrançois Tigeot } 29135718399fSFrançois Tigeot 29144dbb207bSFrançois Tigeot prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); 2915b5162e19SFrançois Tigeot if (!prop_enum) 2916b5162e19SFrançois Tigeot return -ENOMEM; 29175718399fSFrançois Tigeot 29185718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 29195718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 29205718399fSFrançois Tigeot prop_enum->value = value; 29215718399fSFrançois Tigeot 29225718399fSFrançois Tigeot property->values[index] = value; 29235718399fSFrançois Tigeot list_add_tail(&prop_enum->head, &property->enum_blob_list); 29245718399fSFrançois Tigeot return 0; 29255718399fSFrançois Tigeot } 2926b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum); 29275718399fSFrançois Tigeot 29285718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property) 29295718399fSFrançois Tigeot { 29305718399fSFrançois Tigeot struct drm_property_enum *prop_enum, *pt; 29315718399fSFrançois Tigeot 29325718399fSFrançois Tigeot list_for_each_entry_safe(prop_enum, pt, &property->enum_blob_list, head) { 29335718399fSFrançois Tigeot list_del(&prop_enum->head); 29344dbb207bSFrançois Tigeot kfree(prop_enum); 29355718399fSFrançois Tigeot } 29365718399fSFrançois Tigeot 29375718399fSFrançois Tigeot if (property->num_values) 29384dbb207bSFrançois Tigeot kfree(property->values); 29395718399fSFrançois Tigeot drm_mode_object_put(dev, &property->base); 29405718399fSFrançois Tigeot list_del(&property->head); 29414dbb207bSFrançois Tigeot kfree(property); 29425718399fSFrançois Tigeot } 2943b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy); 29445718399fSFrançois Tigeot 2945b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj, 2946b5162e19SFrançois Tigeot struct drm_property *property, 2947b5162e19SFrançois Tigeot uint64_t init_val) 2948b5162e19SFrançois Tigeot { 2949b5162e19SFrançois Tigeot int count = obj->properties->count; 2950b5162e19SFrançois Tigeot 2951b5162e19SFrançois Tigeot if (count == DRM_OBJECT_MAX_PROPERTY) { 2952b5162e19SFrançois Tigeot WARN(1, "Failed to attach object property (type: 0x%x). Please " 2953b5162e19SFrançois Tigeot "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " 2954b5162e19SFrançois Tigeot "you see this message on the same object type.\n", 2955b5162e19SFrançois Tigeot obj->type); 2956b5162e19SFrançois Tigeot return; 2957b5162e19SFrançois Tigeot } 2958b5162e19SFrançois Tigeot 2959b5162e19SFrançois Tigeot obj->properties->ids[count] = property->base.id; 2960b5162e19SFrançois Tigeot obj->properties->values[count] = init_val; 2961b5162e19SFrançois Tigeot obj->properties->count++; 2962b5162e19SFrançois Tigeot } 2963b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property); 2964b5162e19SFrançois Tigeot 2965b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj, 2966b5162e19SFrançois Tigeot struct drm_property *property, uint64_t val) 29675718399fSFrançois Tigeot { 29685718399fSFrançois Tigeot int i; 29695718399fSFrançois Tigeot 2970b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 2971b5162e19SFrançois Tigeot if (obj->properties->ids[i] == property->base.id) { 2972b5162e19SFrançois Tigeot obj->properties->values[i] = val; 29735718399fSFrançois Tigeot return 0; 29745718399fSFrançois Tigeot } 29755718399fSFrançois Tigeot } 29765718399fSFrançois Tigeot 29775718399fSFrançois Tigeot return -EINVAL; 29785718399fSFrançois Tigeot } 2979b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value); 29805718399fSFrançois Tigeot 2981b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj, 29825718399fSFrançois Tigeot struct drm_property *property, uint64_t *val) 29835718399fSFrançois Tigeot { 29845718399fSFrançois Tigeot int i; 29855718399fSFrançois Tigeot 2986b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 2987b5162e19SFrançois Tigeot if (obj->properties->ids[i] == property->base.id) { 2988b5162e19SFrançois Tigeot *val = obj->properties->values[i]; 2989b5162e19SFrançois Tigeot return 0; 29905718399fSFrançois Tigeot } 29915718399fSFrançois Tigeot } 29925718399fSFrançois Tigeot 29935718399fSFrançois Tigeot return -EINVAL; 29945718399fSFrançois Tigeot } 2995b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value); 29965718399fSFrançois Tigeot 29975718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev, 29985718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 29995718399fSFrançois Tigeot { 30005718399fSFrançois Tigeot struct drm_mode_object *obj; 30015718399fSFrançois Tigeot struct drm_mode_get_property *out_resp = data; 30025718399fSFrançois Tigeot struct drm_property *property; 30035718399fSFrançois Tigeot int enum_count = 0; 30045718399fSFrançois Tigeot int blob_count = 0; 30055718399fSFrançois Tigeot int value_count = 0; 30065718399fSFrançois Tigeot int ret = 0, i; 30075718399fSFrançois Tigeot int copied; 30085718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 30095718399fSFrançois Tigeot struct drm_mode_property_enum __user *enum_ptr; 30105718399fSFrançois Tigeot struct drm_property_blob *prop_blob; 3011b5162e19SFrançois Tigeot uint32_t __user *blob_id_ptr; 3012b5162e19SFrançois Tigeot uint64_t __user *values_ptr; 3013b5162e19SFrançois Tigeot uint32_t __user *blob_length_ptr; 30145718399fSFrançois Tigeot 30155718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 30165718399fSFrançois Tigeot return -EINVAL; 30175718399fSFrançois Tigeot 30184dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 30195718399fSFrançois Tigeot obj = drm_mode_object_find(dev, out_resp->prop_id, DRM_MODE_OBJECT_PROPERTY); 30205718399fSFrançois Tigeot if (!obj) { 3021*9edbd4a0SFrançois Tigeot ret = -ENOENT; 30225718399fSFrançois Tigeot goto done; 30235718399fSFrançois Tigeot } 30245718399fSFrançois Tigeot property = obj_to_property(obj); 30255718399fSFrançois Tigeot 3026b5162e19SFrançois Tigeot if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { 30275718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) 30285718399fSFrançois Tigeot enum_count++; 30295718399fSFrançois Tigeot } else if (property->flags & DRM_MODE_PROP_BLOB) { 30305718399fSFrançois Tigeot list_for_each_entry(prop_blob, &property->enum_blob_list, head) 30315718399fSFrançois Tigeot blob_count++; 30325718399fSFrançois Tigeot } 30335718399fSFrançois Tigeot 30345718399fSFrançois Tigeot value_count = property->num_values; 30355718399fSFrançois Tigeot 30365718399fSFrançois Tigeot strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); 30375718399fSFrançois Tigeot out_resp->name[DRM_PROP_NAME_LEN-1] = 0; 30385718399fSFrançois Tigeot out_resp->flags = property->flags; 30395718399fSFrançois Tigeot 30405718399fSFrançois Tigeot if ((out_resp->count_values >= value_count) && value_count) { 3041b5162e19SFrançois Tigeot values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; 30425718399fSFrançois Tigeot for (i = 0; i < value_count; i++) { 3043b5162e19SFrançois Tigeot if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { 30445718399fSFrançois Tigeot ret = -EFAULT; 30455718399fSFrançois Tigeot goto done; 30465718399fSFrançois Tigeot } 30475718399fSFrançois Tigeot } 30485718399fSFrançois Tigeot } 30495718399fSFrançois Tigeot out_resp->count_values = value_count; 30505718399fSFrançois Tigeot 3051b5162e19SFrançois Tigeot if (property->flags & (DRM_MODE_PROP_ENUM | DRM_MODE_PROP_BITMASK)) { 30525718399fSFrançois Tigeot if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { 30535718399fSFrançois Tigeot copied = 0; 3054b5162e19SFrançois Tigeot enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; 30555718399fSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_blob_list, head) { 30565718399fSFrançois Tigeot 3057b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { 30585718399fSFrançois Tigeot ret = -EFAULT; 30595718399fSFrançois Tigeot goto done; 30605718399fSFrançois Tigeot } 30615718399fSFrançois Tigeot 3062b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].name, 3063b5162e19SFrançois Tigeot &prop_enum->name, DRM_PROP_NAME_LEN)) { 30645718399fSFrançois Tigeot ret = -EFAULT; 30655718399fSFrançois Tigeot goto done; 30665718399fSFrançois Tigeot } 30675718399fSFrançois Tigeot copied++; 30685718399fSFrançois Tigeot } 30695718399fSFrançois Tigeot } 30705718399fSFrançois Tigeot out_resp->count_enum_blobs = enum_count; 30715718399fSFrançois Tigeot } 30725718399fSFrançois Tigeot 30735718399fSFrançois Tigeot if (property->flags & DRM_MODE_PROP_BLOB) { 30745718399fSFrançois Tigeot if ((out_resp->count_enum_blobs >= blob_count) && blob_count) { 30755718399fSFrançois Tigeot copied = 0; 3076b5162e19SFrançois Tigeot blob_id_ptr = (uint32_t __user *)(unsigned long)out_resp->enum_blob_ptr; 3077b5162e19SFrançois Tigeot blob_length_ptr = (uint32_t __user *)(unsigned long)out_resp->values_ptr; 30785718399fSFrançois Tigeot 30795718399fSFrançois Tigeot list_for_each_entry(prop_blob, &property->enum_blob_list, head) { 3080b5162e19SFrançois Tigeot if (put_user(prop_blob->base.id, blob_id_ptr + copied)) { 30815718399fSFrançois Tigeot ret = -EFAULT; 30825718399fSFrançois Tigeot goto done; 30835718399fSFrançois Tigeot } 30845718399fSFrançois Tigeot 3085b5162e19SFrançois Tigeot if (put_user(prop_blob->length, blob_length_ptr + copied)) { 30865718399fSFrançois Tigeot ret = -EFAULT; 30875718399fSFrançois Tigeot goto done; 30885718399fSFrançois Tigeot } 30895718399fSFrançois Tigeot 30905718399fSFrançois Tigeot copied++; 30915718399fSFrançois Tigeot } 30925718399fSFrançois Tigeot } 30935718399fSFrançois Tigeot out_resp->count_enum_blobs = blob_count; 30945718399fSFrançois Tigeot } 30955718399fSFrançois Tigeot done: 30964dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 30975718399fSFrançois Tigeot return ret; 30985718399fSFrançois Tigeot } 30995718399fSFrançois Tigeot 31005718399fSFrançois Tigeot static struct drm_property_blob *drm_property_create_blob(struct drm_device *dev, int length, 31015718399fSFrançois Tigeot void *data) 31025718399fSFrançois Tigeot { 31035718399fSFrançois Tigeot struct drm_property_blob *blob; 31045718399fSFrançois Tigeot int ret; 31055718399fSFrançois Tigeot 31065718399fSFrançois Tigeot if (!length || !data) 31075718399fSFrançois Tigeot return NULL; 31085718399fSFrançois Tigeot 31094dbb207bSFrançois Tigeot blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); 3110b5162e19SFrançois Tigeot if (!blob) 3111b5162e19SFrançois Tigeot return NULL; 31125718399fSFrançois Tigeot 31135718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &blob->base, DRM_MODE_OBJECT_BLOB); 31145718399fSFrançois Tigeot if (ret) { 31154dbb207bSFrançois Tigeot kfree(blob); 3116b5162e19SFrançois Tigeot return NULL; 31175718399fSFrançois Tigeot } 31185718399fSFrançois Tigeot 31195718399fSFrançois Tigeot blob->length = length; 31205718399fSFrançois Tigeot 31215718399fSFrançois Tigeot memcpy(blob->data, data, length); 31225718399fSFrançois Tigeot 31235718399fSFrançois Tigeot list_add_tail(&blob->head, &dev->mode_config.property_blob_list); 31245718399fSFrançois Tigeot return blob; 31255718399fSFrançois Tigeot } 31265718399fSFrançois Tigeot 31275718399fSFrançois Tigeot static void drm_property_destroy_blob(struct drm_device *dev, 31285718399fSFrançois Tigeot struct drm_property_blob *blob) 31295718399fSFrançois Tigeot { 31305718399fSFrançois Tigeot drm_mode_object_put(dev, &blob->base); 31315718399fSFrançois Tigeot list_del(&blob->head); 31324dbb207bSFrançois Tigeot kfree(blob); 31335718399fSFrançois Tigeot } 31345718399fSFrançois Tigeot 31355718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev, 31365718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 31375718399fSFrançois Tigeot { 31385718399fSFrançois Tigeot struct drm_mode_object *obj; 31395718399fSFrançois Tigeot struct drm_mode_get_blob *out_resp = data; 31405718399fSFrançois Tigeot struct drm_property_blob *blob; 31415718399fSFrançois Tigeot int ret = 0; 3142b5162e19SFrançois Tigeot void __user *blob_ptr; 31435718399fSFrançois Tigeot 31445718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 31455718399fSFrançois Tigeot return -EINVAL; 31465718399fSFrançois Tigeot 31474dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 31485718399fSFrançois Tigeot obj = drm_mode_object_find(dev, out_resp->blob_id, DRM_MODE_OBJECT_BLOB); 31495718399fSFrançois Tigeot if (!obj) { 3150*9edbd4a0SFrançois Tigeot ret = -ENOENT; 31515718399fSFrançois Tigeot goto done; 31525718399fSFrançois Tigeot } 31535718399fSFrançois Tigeot blob = obj_to_blob(obj); 31545718399fSFrançois Tigeot 31555718399fSFrançois Tigeot if (out_resp->length == blob->length) { 3156b5162e19SFrançois Tigeot blob_ptr = (void __user *)(unsigned long)out_resp->data; 3157b5162e19SFrançois Tigeot if (copy_to_user(blob_ptr, blob->data, blob->length)){ 31585718399fSFrançois Tigeot ret = -EFAULT; 31595718399fSFrançois Tigeot goto done; 31605718399fSFrançois Tigeot } 31615718399fSFrançois Tigeot } 31625718399fSFrançois Tigeot out_resp->length = blob->length; 31635718399fSFrançois Tigeot 31645718399fSFrançois Tigeot done: 31654dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 31665718399fSFrançois Tigeot return ret; 31675718399fSFrançois Tigeot } 31685718399fSFrançois Tigeot 31695718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector, 31705718399fSFrançois Tigeot struct edid *edid) 31715718399fSFrançois Tigeot { 31725718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 3173b5162e19SFrançois Tigeot int ret, size; 31745718399fSFrançois Tigeot 31755718399fSFrançois Tigeot if (connector->edid_blob_ptr) 31765718399fSFrançois Tigeot drm_property_destroy_blob(dev, connector->edid_blob_ptr); 31775718399fSFrançois Tigeot 31785718399fSFrançois Tigeot /* Delete edid, when there is none. */ 31795718399fSFrançois Tigeot if (!edid) { 31805718399fSFrançois Tigeot connector->edid_blob_ptr = NULL; 3181b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, dev->mode_config.edid_property, 0); 31825718399fSFrançois Tigeot return ret; 31835718399fSFrançois Tigeot } 31845718399fSFrançois Tigeot 31855718399fSFrançois Tigeot size = EDID_LENGTH * (1 + edid->extensions); 31865718399fSFrançois Tigeot connector->edid_blob_ptr = drm_property_create_blob(connector->dev, 31875718399fSFrançois Tigeot size, edid); 3188b5162e19SFrançois Tigeot if (!connector->edid_blob_ptr) 3189b5162e19SFrançois Tigeot return -EINVAL; 31905718399fSFrançois Tigeot 3191b5162e19SFrançois Tigeot ret = drm_object_property_set_value(&connector->base, 31925718399fSFrançois Tigeot dev->mode_config.edid_property, 31935718399fSFrançois Tigeot connector->edid_blob_ptr->base.id); 31945718399fSFrançois Tigeot 31955718399fSFrançois Tigeot return ret; 31965718399fSFrançois Tigeot } 3197b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property); 3198b5162e19SFrançois Tigeot 3199b5162e19SFrançois Tigeot static bool drm_property_change_is_valid(struct drm_property *property, 3200b5162e19SFrançois Tigeot uint64_t value) 3201b5162e19SFrançois Tigeot { 3202b5162e19SFrançois Tigeot if (property->flags & DRM_MODE_PROP_IMMUTABLE) 3203b5162e19SFrançois Tigeot return false; 3204b5162e19SFrançois Tigeot if (property->flags & DRM_MODE_PROP_RANGE) { 3205b5162e19SFrançois Tigeot if (value < property->values[0] || value > property->values[1]) 3206b5162e19SFrançois Tigeot return false; 3207b5162e19SFrançois Tigeot return true; 3208b5162e19SFrançois Tigeot } else if (property->flags & DRM_MODE_PROP_BITMASK) { 3209b5162e19SFrançois Tigeot int i; 3210b5162e19SFrançois Tigeot uint64_t valid_mask = 0; 3211b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 3212b5162e19SFrançois Tigeot valid_mask |= (1ULL << property->values[i]); 3213b5162e19SFrançois Tigeot return !(value & ~valid_mask); 3214b5162e19SFrançois Tigeot } else if (property->flags & DRM_MODE_PROP_BLOB) { 3215b5162e19SFrançois Tigeot /* Only the driver knows */ 3216b5162e19SFrançois Tigeot return true; 3217b5162e19SFrançois Tigeot } else { 3218b5162e19SFrançois Tigeot int i; 3219b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 3220b5162e19SFrançois Tigeot if (property->values[i] == value) 3221b5162e19SFrançois Tigeot return true; 3222b5162e19SFrançois Tigeot return false; 3223b5162e19SFrançois Tigeot } 3224b5162e19SFrançois Tigeot } 32255718399fSFrançois Tigeot 32265718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev, 32275718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 32285718399fSFrançois Tigeot { 3229b5162e19SFrançois Tigeot struct drm_mode_connector_set_property *conn_set_prop = data; 3230b5162e19SFrançois Tigeot struct drm_mode_obj_set_property obj_set_prop = { 3231b5162e19SFrançois Tigeot .value = conn_set_prop->value, 3232b5162e19SFrançois Tigeot .prop_id = conn_set_prop->prop_id, 3233b5162e19SFrançois Tigeot .obj_id = conn_set_prop->connector_id, 3234b5162e19SFrançois Tigeot .obj_type = DRM_MODE_OBJECT_CONNECTOR 3235b5162e19SFrançois Tigeot }; 3236b5162e19SFrançois Tigeot 3237b5162e19SFrançois Tigeot /* It does all the locking and checking we need */ 3238b5162e19SFrançois Tigeot return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); 3239b5162e19SFrançois Tigeot } 3240b5162e19SFrançois Tigeot 3241b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, 3242b5162e19SFrançois Tigeot struct drm_property *property, 3243b5162e19SFrançois Tigeot uint64_t value) 3244b5162e19SFrançois Tigeot { 3245b5162e19SFrançois Tigeot int ret = -EINVAL; 3246b5162e19SFrançois Tigeot struct drm_connector *connector = obj_to_connector(obj); 3247b5162e19SFrançois Tigeot 3248b5162e19SFrançois Tigeot /* Do DPMS ourselves */ 3249b5162e19SFrançois Tigeot if (property == connector->dev->mode_config.dpms_property) { 3250b5162e19SFrançois Tigeot if (connector->funcs->dpms) 3251b5162e19SFrançois Tigeot (*connector->funcs->dpms)(connector, (int)value); 3252b5162e19SFrançois Tigeot ret = 0; 3253b5162e19SFrançois Tigeot } else if (connector->funcs->set_property) 3254b5162e19SFrançois Tigeot ret = connector->funcs->set_property(connector, property, value); 3255b5162e19SFrançois Tigeot 3256b5162e19SFrançois Tigeot /* store the property value if successful */ 3257b5162e19SFrançois Tigeot if (!ret) 3258b5162e19SFrançois Tigeot drm_object_property_set_value(&connector->base, property, value); 3259b5162e19SFrançois Tigeot return ret; 3260b5162e19SFrançois Tigeot } 3261b5162e19SFrançois Tigeot 3262b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, 3263b5162e19SFrançois Tigeot struct drm_property *property, 3264b5162e19SFrançois Tigeot uint64_t value) 3265b5162e19SFrançois Tigeot { 3266b5162e19SFrançois Tigeot int ret = -EINVAL; 3267b5162e19SFrançois Tigeot struct drm_crtc *crtc = obj_to_crtc(obj); 3268b5162e19SFrançois Tigeot 3269b5162e19SFrançois Tigeot if (crtc->funcs->set_property) 3270b5162e19SFrançois Tigeot ret = crtc->funcs->set_property(crtc, property, value); 3271b5162e19SFrançois Tigeot if (!ret) 3272b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 3273b5162e19SFrançois Tigeot 3274b5162e19SFrançois Tigeot return ret; 3275b5162e19SFrançois Tigeot } 3276b5162e19SFrançois Tigeot 3277b5162e19SFrançois Tigeot static int drm_mode_plane_set_obj_prop(struct drm_mode_object *obj, 3278b5162e19SFrançois Tigeot struct drm_property *property, 3279b5162e19SFrançois Tigeot uint64_t value) 3280b5162e19SFrançois Tigeot { 3281b5162e19SFrançois Tigeot int ret = -EINVAL; 3282b5162e19SFrançois Tigeot struct drm_plane *plane = obj_to_plane(obj); 3283b5162e19SFrançois Tigeot 3284b5162e19SFrançois Tigeot if (plane->funcs->set_property) 3285b5162e19SFrançois Tigeot ret = plane->funcs->set_property(plane, property, value); 3286b5162e19SFrançois Tigeot if (!ret) 3287b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 3288b5162e19SFrançois Tigeot 3289b5162e19SFrançois Tigeot return ret; 3290b5162e19SFrançois Tigeot } 3291b5162e19SFrançois Tigeot 3292b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, 3293b5162e19SFrançois Tigeot struct drm_file *file_priv) 3294b5162e19SFrançois Tigeot { 3295b5162e19SFrançois Tigeot struct drm_mode_obj_get_properties *arg = data; 32965718399fSFrançois Tigeot struct drm_mode_object *obj; 3297b5162e19SFrançois Tigeot int ret = 0; 3298b5162e19SFrançois Tigeot int i; 3299b5162e19SFrançois Tigeot int copied = 0; 3300b5162e19SFrançois Tigeot int props_count = 0; 3301b5162e19SFrançois Tigeot uint32_t __user *props_ptr; 3302b5162e19SFrançois Tigeot uint64_t __user *prop_values_ptr; 3303b5162e19SFrançois Tigeot 3304b5162e19SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3305b5162e19SFrançois Tigeot return -EINVAL; 3306b5162e19SFrançois Tigeot 33074dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 3308b5162e19SFrançois Tigeot 3309b5162e19SFrançois Tigeot obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 3310b5162e19SFrançois Tigeot if (!obj) { 3311*9edbd4a0SFrançois Tigeot ret = -ENOENT; 3312b5162e19SFrançois Tigeot goto out; 3313b5162e19SFrançois Tigeot } 3314b5162e19SFrançois Tigeot if (!obj->properties) { 3315b5162e19SFrançois Tigeot ret = -EINVAL; 3316b5162e19SFrançois Tigeot goto out; 3317b5162e19SFrançois Tigeot } 3318b5162e19SFrançois Tigeot 3319b5162e19SFrançois Tigeot props_count = obj->properties->count; 3320b5162e19SFrançois Tigeot 3321b5162e19SFrançois Tigeot /* This ioctl is called twice, once to determine how much space is 3322b5162e19SFrançois Tigeot * needed, and the 2nd time to fill it. */ 3323b5162e19SFrançois Tigeot if ((arg->count_props >= props_count) && props_count) { 3324b5162e19SFrançois Tigeot copied = 0; 3325b5162e19SFrançois Tigeot props_ptr = (uint32_t __user *)(unsigned long)(arg->props_ptr); 3326b5162e19SFrançois Tigeot prop_values_ptr = (uint64_t __user *)(unsigned long) 3327b5162e19SFrançois Tigeot (arg->prop_values_ptr); 3328b5162e19SFrançois Tigeot for (i = 0; i < props_count; i++) { 3329b5162e19SFrançois Tigeot if (put_user(obj->properties->ids[i], 3330b5162e19SFrançois Tigeot props_ptr + copied)) { 3331b5162e19SFrançois Tigeot ret = -EFAULT; 3332b5162e19SFrançois Tigeot goto out; 3333b5162e19SFrançois Tigeot } 3334b5162e19SFrançois Tigeot if (put_user(obj->properties->values[i], 3335b5162e19SFrançois Tigeot prop_values_ptr + copied)) { 3336b5162e19SFrançois Tigeot ret = -EFAULT; 3337b5162e19SFrançois Tigeot goto out; 3338b5162e19SFrançois Tigeot } 3339b5162e19SFrançois Tigeot copied++; 3340b5162e19SFrançois Tigeot } 3341b5162e19SFrançois Tigeot } 3342b5162e19SFrançois Tigeot arg->count_props = props_count; 3343b5162e19SFrançois Tigeot out: 33444dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 3345b5162e19SFrançois Tigeot return ret; 3346b5162e19SFrançois Tigeot } 3347b5162e19SFrançois Tigeot 3348b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, 3349b5162e19SFrançois Tigeot struct drm_file *file_priv) 3350b5162e19SFrançois Tigeot { 3351b5162e19SFrançois Tigeot struct drm_mode_obj_set_property *arg = data; 3352b5162e19SFrançois Tigeot struct drm_mode_object *arg_obj; 3353b5162e19SFrançois Tigeot struct drm_mode_object *prop_obj; 33545718399fSFrançois Tigeot struct drm_property *property; 33555718399fSFrançois Tigeot int ret = -EINVAL; 33565718399fSFrançois Tigeot int i; 33575718399fSFrançois Tigeot 33585718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 33595718399fSFrançois Tigeot return -EINVAL; 33605718399fSFrançois Tigeot 33614dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 33625718399fSFrançois Tigeot 3363b5162e19SFrançois Tigeot arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 3364*9edbd4a0SFrançois Tigeot if (!arg_obj) { 3365*9edbd4a0SFrançois Tigeot ret = -ENOENT; 33665718399fSFrançois Tigeot goto out; 3367*9edbd4a0SFrançois Tigeot } 3368b5162e19SFrançois Tigeot if (!arg_obj->properties) 3369b5162e19SFrançois Tigeot goto out; 33705718399fSFrançois Tigeot 3371b5162e19SFrançois Tigeot for (i = 0; i < arg_obj->properties->count; i++) 3372b5162e19SFrançois Tigeot if (arg_obj->properties->ids[i] == arg->prop_id) 3373b5162e19SFrançois Tigeot break; 3374b5162e19SFrançois Tigeot 3375b5162e19SFrançois Tigeot if (i == arg_obj->properties->count) 3376b5162e19SFrançois Tigeot goto out; 3377b5162e19SFrançois Tigeot 3378b5162e19SFrançois Tigeot prop_obj = drm_mode_object_find(dev, arg->prop_id, 3379b5162e19SFrançois Tigeot DRM_MODE_OBJECT_PROPERTY); 3380*9edbd4a0SFrançois Tigeot if (!prop_obj) { 3381*9edbd4a0SFrançois Tigeot ret = -ENOENT; 3382b5162e19SFrançois Tigeot goto out; 3383*9edbd4a0SFrançois Tigeot } 3384b5162e19SFrançois Tigeot property = obj_to_property(prop_obj); 3385b5162e19SFrançois Tigeot 3386b5162e19SFrançois Tigeot if (!drm_property_change_is_valid(property, arg->value)) 3387b5162e19SFrançois Tigeot goto out; 3388b5162e19SFrançois Tigeot 3389b5162e19SFrançois Tigeot switch (arg_obj->type) { 3390b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CONNECTOR: 3391b5162e19SFrançois Tigeot ret = drm_mode_connector_set_obj_prop(arg_obj, property, 3392b5162e19SFrançois Tigeot arg->value); 3393b5162e19SFrançois Tigeot break; 3394b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CRTC: 3395b5162e19SFrançois Tigeot ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); 3396b5162e19SFrançois Tigeot break; 3397b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_PLANE: 3398b5162e19SFrançois Tigeot ret = drm_mode_plane_set_obj_prop(arg_obj, property, arg->value); 33995718399fSFrançois Tigeot break; 34005718399fSFrançois Tigeot } 34015718399fSFrançois Tigeot 34025718399fSFrançois Tigeot out: 34034dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 34045718399fSFrançois Tigeot return ret; 34055718399fSFrançois Tigeot } 34065718399fSFrançois Tigeot 34075718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector, 34085718399fSFrançois Tigeot struct drm_encoder *encoder) 34095718399fSFrançois Tigeot { 34105718399fSFrançois Tigeot int i; 34115718399fSFrançois Tigeot 34125718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 34135718399fSFrançois Tigeot if (connector->encoder_ids[i] == 0) { 34145718399fSFrançois Tigeot connector->encoder_ids[i] = encoder->base.id; 34155718399fSFrançois Tigeot return 0; 34165718399fSFrançois Tigeot } 34175718399fSFrançois Tigeot } 34185718399fSFrançois Tigeot return -ENOMEM; 34195718399fSFrançois Tigeot } 3420b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder); 34215718399fSFrançois Tigeot 34225718399fSFrançois Tigeot void drm_mode_connector_detach_encoder(struct drm_connector *connector, 34235718399fSFrançois Tigeot struct drm_encoder *encoder) 34245718399fSFrançois Tigeot { 34255718399fSFrançois Tigeot int i; 34265718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 34275718399fSFrançois Tigeot if (connector->encoder_ids[i] == encoder->base.id) { 34285718399fSFrançois Tigeot connector->encoder_ids[i] = 0; 34295718399fSFrançois Tigeot if (connector->encoder == encoder) 34305718399fSFrançois Tigeot connector->encoder = NULL; 34315718399fSFrançois Tigeot break; 34325718399fSFrançois Tigeot } 34335718399fSFrançois Tigeot } 34345718399fSFrançois Tigeot } 3435b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_detach_encoder); 34365718399fSFrançois Tigeot 34375718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, 34385718399fSFrançois Tigeot int gamma_size) 34395718399fSFrançois Tigeot { 34405718399fSFrançois Tigeot crtc->gamma_size = gamma_size; 34415718399fSFrançois Tigeot 34424dbb207bSFrançois Tigeot crtc->gamma_store = kzalloc(gamma_size * sizeof(uint16_t) * 3, GFP_KERNEL); 3443b5162e19SFrançois Tigeot if (!crtc->gamma_store) { 3444b5162e19SFrançois Tigeot crtc->gamma_size = 0; 3445b5162e19SFrançois Tigeot return -ENOMEM; 3446b5162e19SFrançois Tigeot } 34475718399fSFrançois Tigeot 34485718399fSFrançois Tigeot return 0; 34495718399fSFrançois Tigeot } 3450b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); 34515718399fSFrançois Tigeot 34525718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev, 34535718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 34545718399fSFrançois Tigeot { 34555718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 34565718399fSFrançois Tigeot struct drm_mode_object *obj; 34575718399fSFrançois Tigeot struct drm_crtc *crtc; 34585718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 34595718399fSFrançois Tigeot int size; 34605718399fSFrançois Tigeot int ret = 0; 34615718399fSFrançois Tigeot 34625718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 34635718399fSFrançois Tigeot return -EINVAL; 34645718399fSFrançois Tigeot 34654dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 34665718399fSFrançois Tigeot obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 34675718399fSFrançois Tigeot if (!obj) { 3468*9edbd4a0SFrançois Tigeot ret = -ENOENT; 34695718399fSFrançois Tigeot goto out; 34705718399fSFrançois Tigeot } 34715718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 34725718399fSFrançois Tigeot 3473b5162e19SFrançois Tigeot if (crtc->funcs->gamma_set == NULL) { 3474b5162e19SFrançois Tigeot ret = -ENOSYS; 3475b5162e19SFrançois Tigeot goto out; 3476b5162e19SFrançois Tigeot } 3477b5162e19SFrançois Tigeot 34785718399fSFrançois Tigeot /* memcpy into gamma store */ 34795718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 34805718399fSFrançois Tigeot ret = -EINVAL; 34815718399fSFrançois Tigeot goto out; 34825718399fSFrançois Tigeot } 34835718399fSFrançois Tigeot 34845718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 34855718399fSFrançois Tigeot r_base = crtc->gamma_store; 3486b5162e19SFrançois Tigeot if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { 34875718399fSFrançois Tigeot ret = -EFAULT; 34885718399fSFrançois Tigeot goto out; 34895718399fSFrançois Tigeot } 34905718399fSFrançois Tigeot 34915718399fSFrançois Tigeot g_base = (char *)r_base + size; 3492b5162e19SFrançois Tigeot if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { 34935718399fSFrançois Tigeot ret = -EFAULT; 34945718399fSFrançois Tigeot goto out; 34955718399fSFrançois Tigeot } 34965718399fSFrançois Tigeot 349706fede5aSFrançois Tigeot b_base = (char *)g_base + size; 3498b5162e19SFrançois Tigeot if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { 34995718399fSFrançois Tigeot ret = -EFAULT; 35005718399fSFrançois Tigeot goto out; 35015718399fSFrançois Tigeot } 35025718399fSFrançois Tigeot 35035718399fSFrançois Tigeot crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 35045718399fSFrançois Tigeot 35055718399fSFrançois Tigeot out: 35064dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 35075718399fSFrançois Tigeot return ret; 35085718399fSFrançois Tigeot 35095718399fSFrançois Tigeot } 35105718399fSFrançois Tigeot 35115718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev, 35125718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 35135718399fSFrançois Tigeot { 35145718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 35155718399fSFrançois Tigeot struct drm_mode_object *obj; 35165718399fSFrançois Tigeot struct drm_crtc *crtc; 35175718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 35185718399fSFrançois Tigeot int size; 35195718399fSFrançois Tigeot int ret = 0; 35205718399fSFrançois Tigeot 35215718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 35225718399fSFrançois Tigeot return -EINVAL; 35235718399fSFrançois Tigeot 35244dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 35255718399fSFrançois Tigeot obj = drm_mode_object_find(dev, crtc_lut->crtc_id, DRM_MODE_OBJECT_CRTC); 35265718399fSFrançois Tigeot if (!obj) { 3527*9edbd4a0SFrançois Tigeot ret = -ENOENT; 35285718399fSFrançois Tigeot goto out; 35295718399fSFrançois Tigeot } 35305718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 35315718399fSFrançois Tigeot 35325718399fSFrançois Tigeot /* memcpy into gamma store */ 35335718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 35345718399fSFrançois Tigeot ret = -EINVAL; 35355718399fSFrançois Tigeot goto out; 35365718399fSFrançois Tigeot } 35375718399fSFrançois Tigeot 35385718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 35395718399fSFrançois Tigeot r_base = crtc->gamma_store; 3540b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { 35415718399fSFrançois Tigeot ret = -EFAULT; 35425718399fSFrançois Tigeot goto out; 35435718399fSFrançois Tigeot } 35445718399fSFrançois Tigeot 35455718399fSFrançois Tigeot g_base = (char *)r_base + size; 3546b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { 35475718399fSFrançois Tigeot ret = -EFAULT; 35485718399fSFrançois Tigeot goto out; 35495718399fSFrançois Tigeot } 35505718399fSFrançois Tigeot 35515718399fSFrançois Tigeot b_base = (char *)g_base + size; 3552b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { 35535718399fSFrançois Tigeot ret = -EFAULT; 35545718399fSFrançois Tigeot goto out; 35555718399fSFrançois Tigeot } 35565718399fSFrançois Tigeot out: 35574dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 35585718399fSFrançois Tigeot return ret; 35595718399fSFrançois Tigeot } 35605718399fSFrançois Tigeot 3561a5f72378SFrançois Tigeot /* 3562a5f72378SFrançois Tigeot * The Linux version of kfree() is a macro and can't be called 3563a5f72378SFrançois Tigeot * directly via a function pointer 3564a5f72378SFrançois Tigeot */ 3565a5f72378SFrançois Tigeot static void drm_kms_free(void *arg) 3566a5f72378SFrançois Tigeot { 3567a5f72378SFrançois Tigeot kfree(arg); 3568a5f72378SFrançois Tigeot } 3569a5f72378SFrançois Tigeot 3570b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev, 3571b5162e19SFrançois Tigeot void *data, struct drm_file *file_priv) 35725718399fSFrançois Tigeot { 35735718399fSFrançois Tigeot struct drm_mode_crtc_page_flip *page_flip = data; 35745718399fSFrançois Tigeot struct drm_mode_object *obj; 35755718399fSFrançois Tigeot struct drm_crtc *crtc; 35764dbb207bSFrançois Tigeot struct drm_framebuffer *fb = NULL, *old_fb = NULL; 35775718399fSFrançois Tigeot struct drm_pending_vblank_event *e = NULL; 3578b5162e19SFrançois Tigeot int ret = -EINVAL; 35795718399fSFrançois Tigeot 35805718399fSFrançois Tigeot if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || 35815718399fSFrançois Tigeot page_flip->reserved != 0) 3582b5162e19SFrançois Tigeot return -EINVAL; 35835718399fSFrançois Tigeot 3584*9edbd4a0SFrançois Tigeot if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) 3585*9edbd4a0SFrançois Tigeot return -EINVAL; 3586*9edbd4a0SFrançois Tigeot 35875718399fSFrançois Tigeot obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC); 35885718399fSFrançois Tigeot if (!obj) 3589*9edbd4a0SFrançois Tigeot return -ENOENT; 35905718399fSFrançois Tigeot crtc = obj_to_crtc(obj); 35915718399fSFrançois Tigeot 35924dbb207bSFrançois Tigeot mutex_lock(&crtc->mutex); 35935718399fSFrançois Tigeot if (crtc->fb == NULL) { 35945718399fSFrançois Tigeot /* The framebuffer is currently unbound, presumably 35955718399fSFrançois Tigeot * due to a hotplug event, that userspace has not 35965718399fSFrançois Tigeot * yet discovered. 35975718399fSFrançois Tigeot */ 3598b5162e19SFrançois Tigeot ret = -EBUSY; 35995718399fSFrançois Tigeot goto out; 36005718399fSFrançois Tigeot } 36015718399fSFrançois Tigeot 36025718399fSFrançois Tigeot if (crtc->funcs->page_flip == NULL) 36035718399fSFrançois Tigeot goto out; 36045718399fSFrançois Tigeot 36054dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, page_flip->fb_id); 3606*9edbd4a0SFrançois Tigeot if (!fb) { 3607*9edbd4a0SFrançois Tigeot ret = -ENOENT; 36085718399fSFrançois Tigeot goto out; 36095718399fSFrançois Tigeot } 36105718399fSFrançois Tigeot 3611*9edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); 3612*9edbd4a0SFrançois Tigeot if (ret) 3613*9edbd4a0SFrançois Tigeot goto out; 3614*9edbd4a0SFrançois Tigeot 36154dbb207bSFrançois Tigeot if (crtc->fb->pixel_format != fb->pixel_format) { 36164dbb207bSFrançois Tigeot DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); 36174dbb207bSFrançois Tigeot ret = -EINVAL; 36184dbb207bSFrançois Tigeot goto out; 36194dbb207bSFrançois Tigeot } 36204dbb207bSFrançois Tigeot 36215718399fSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 3622b5162e19SFrançois Tigeot ret = -ENOMEM; 36235718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 36245718399fSFrançois Tigeot if (file_priv->event_space < sizeof e->event) { 36255718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 36265718399fSFrançois Tigeot goto out; 36275718399fSFrançois Tigeot } 36285718399fSFrançois Tigeot file_priv->event_space -= sizeof e->event; 36295718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 36305718399fSFrançois Tigeot 36314dbb207bSFrançois Tigeot e = kzalloc(sizeof *e, GFP_KERNEL); 3632b5162e19SFrançois Tigeot if (e == NULL) { 3633b5162e19SFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 3634b5162e19SFrançois Tigeot file_priv->event_space += sizeof e->event; 3635b5162e19SFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 3636b5162e19SFrançois Tigeot goto out; 3637b5162e19SFrançois Tigeot } 36385718399fSFrançois Tigeot 36395718399fSFrançois Tigeot e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 36405718399fSFrançois Tigeot e->event.base.length = sizeof e->event; 36415718399fSFrançois Tigeot e->event.user_data = page_flip->user_data; 36425718399fSFrançois Tigeot e->base.event = &e->event.base; 36435718399fSFrançois Tigeot e->base.file_priv = file_priv; 36445718399fSFrançois Tigeot e->base.destroy = 3645a5f72378SFrançois Tigeot (void (*) (struct drm_pending_event *)) drm_kms_free; 36465718399fSFrançois Tigeot } 36475718399fSFrançois Tigeot 36484dbb207bSFrançois Tigeot old_fb = crtc->fb; 3649*9edbd4a0SFrançois Tigeot ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); 3650b5162e19SFrançois Tigeot if (ret) { 36515718399fSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 36525718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_EXCLUSIVE); 36535718399fSFrançois Tigeot file_priv->event_space += sizeof e->event; 36545718399fSFrançois Tigeot lockmgr(&dev->event_lock, LK_RELEASE); 36554dbb207bSFrançois Tigeot kfree(e); 36565718399fSFrançois Tigeot } 36574dbb207bSFrançois Tigeot /* Keep the old fb, don't unref it. */ 36584dbb207bSFrançois Tigeot old_fb = NULL; 36594dbb207bSFrançois Tigeot } else { 36604dbb207bSFrançois Tigeot /* 36614dbb207bSFrançois Tigeot * Warn if the driver hasn't properly updated the crtc->fb 36624dbb207bSFrançois Tigeot * field to reflect that the new framebuffer is now used. 36634dbb207bSFrançois Tigeot * Failing to do so will screw with the reference counting 36644dbb207bSFrançois Tigeot * on framebuffers. 36654dbb207bSFrançois Tigeot */ 36664dbb207bSFrançois Tigeot WARN_ON(crtc->fb != fb); 36674dbb207bSFrançois Tigeot /* Unref only the old framebuffer. */ 36684dbb207bSFrançois Tigeot fb = NULL; 36695718399fSFrançois Tigeot } 36705718399fSFrançois Tigeot 36715718399fSFrançois Tigeot out: 36724dbb207bSFrançois Tigeot if (fb) 36734dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 36744dbb207bSFrançois Tigeot if (old_fb) 36754dbb207bSFrançois Tigeot drm_framebuffer_unreference(old_fb); 36764dbb207bSFrançois Tigeot mutex_unlock(&crtc->mutex); 36774dbb207bSFrançois Tigeot 3678b5162e19SFrançois Tigeot return ret; 36795718399fSFrançois Tigeot } 36805718399fSFrançois Tigeot 36815718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev) 36825718399fSFrançois Tigeot { 36835718399fSFrançois Tigeot struct drm_crtc *crtc; 36845718399fSFrançois Tigeot struct drm_encoder *encoder; 36855718399fSFrançois Tigeot struct drm_connector *connector; 36865718399fSFrançois Tigeot 36875718399fSFrançois Tigeot list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) 36885718399fSFrançois Tigeot if (crtc->funcs->reset) 36895718399fSFrançois Tigeot crtc->funcs->reset(crtc); 36905718399fSFrançois Tigeot 36915718399fSFrançois Tigeot list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) 36925718399fSFrançois Tigeot if (encoder->funcs->reset) 36935718399fSFrançois Tigeot encoder->funcs->reset(encoder); 36945718399fSFrançois Tigeot 3695b5162e19SFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) { 3696b5162e19SFrançois Tigeot connector->status = connector_status_unknown; 3697b5162e19SFrançois Tigeot 36985718399fSFrançois Tigeot if (connector->funcs->reset) 36995718399fSFrançois Tigeot connector->funcs->reset(connector); 37005718399fSFrançois Tigeot } 3701b5162e19SFrançois Tigeot } 3702b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset); 37035718399fSFrançois Tigeot 37045718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev, 37055718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 37065718399fSFrançois Tigeot { 37075718399fSFrançois Tigeot struct drm_mode_create_dumb *args = data; 37085718399fSFrançois Tigeot 37095718399fSFrançois Tigeot if (!dev->driver->dumb_create) 3710b5162e19SFrançois Tigeot return -ENOSYS; 37115718399fSFrançois Tigeot return dev->driver->dumb_create(file_priv, dev, args); 37125718399fSFrançois Tigeot } 37135718399fSFrançois Tigeot 37145718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, 37155718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 37165718399fSFrançois Tigeot { 37175718399fSFrançois Tigeot struct drm_mode_map_dumb *args = data; 37185718399fSFrançois Tigeot 37195718399fSFrançois Tigeot /* call driver ioctl to get mmap offset */ 37205718399fSFrançois Tigeot if (!dev->driver->dumb_map_offset) 3721b5162e19SFrançois Tigeot return -ENOSYS; 37225718399fSFrançois Tigeot 37235718399fSFrançois Tigeot return dev->driver->dumb_map_offset(file_priv, dev, args->handle, &args->offset); 37245718399fSFrançois Tigeot } 37255718399fSFrançois Tigeot 37265718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, 37275718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 37285718399fSFrançois Tigeot { 37295718399fSFrançois Tigeot struct drm_mode_destroy_dumb *args = data; 37305718399fSFrançois Tigeot 37315718399fSFrançois Tigeot if (!dev->driver->dumb_destroy) 3732b5162e19SFrançois Tigeot return -ENOSYS; 37335718399fSFrançois Tigeot 37345718399fSFrançois Tigeot return dev->driver->dumb_destroy(file_priv, dev, args->handle); 37355718399fSFrançois Tigeot } 37365718399fSFrançois Tigeot 37375718399fSFrançois Tigeot /* 37385718399fSFrançois Tigeot * Just need to support RGB formats here for compat with code that doesn't 37395718399fSFrançois Tigeot * use pixel formats directly yet. 37405718399fSFrançois Tigeot */ 37415718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, 37425718399fSFrançois Tigeot int *bpp) 37435718399fSFrançois Tigeot { 37445718399fSFrançois Tigeot switch (format) { 37454dbb207bSFrançois Tigeot case DRM_FORMAT_C8: 37465718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 37475718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 37485718399fSFrançois Tigeot *depth = 8; 37495718399fSFrançois Tigeot *bpp = 8; 37505718399fSFrançois Tigeot break; 37515718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 37525718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 37535718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 37545718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 37555718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 37565718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 37575718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 37585718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 37595718399fSFrançois Tigeot *depth = 15; 37605718399fSFrançois Tigeot *bpp = 16; 37615718399fSFrançois Tigeot break; 37625718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 37635718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 37645718399fSFrançois Tigeot *depth = 16; 37655718399fSFrançois Tigeot *bpp = 16; 37665718399fSFrançois Tigeot break; 37675718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 37685718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 37695718399fSFrançois Tigeot *depth = 24; 37705718399fSFrançois Tigeot *bpp = 24; 37715718399fSFrançois Tigeot break; 37725718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 37735718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 37745718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 37755718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 37765718399fSFrançois Tigeot *depth = 24; 37775718399fSFrançois Tigeot *bpp = 32; 37785718399fSFrançois Tigeot break; 37795718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 37805718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 37815718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 37825718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 37835718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 37845718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 37855718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 37865718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 37875718399fSFrançois Tigeot *depth = 30; 37885718399fSFrançois Tigeot *bpp = 32; 37895718399fSFrançois Tigeot break; 37905718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 37915718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 37925718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 37935718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 37945718399fSFrançois Tigeot *depth = 32; 37955718399fSFrançois Tigeot *bpp = 32; 37965718399fSFrançois Tigeot break; 37975718399fSFrançois Tigeot default: 3798*9edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("unsupported pixel format %s\n", 3799*9edbd4a0SFrançois Tigeot drm_get_format_name(format)); 38005718399fSFrançois Tigeot *depth = 0; 38015718399fSFrançois Tigeot *bpp = 0; 38025718399fSFrançois Tigeot break; 38035718399fSFrançois Tigeot } 38045718399fSFrançois Tigeot } 3805b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth); 3806b5162e19SFrançois Tigeot 3807b5162e19SFrançois Tigeot /** 3808b5162e19SFrançois Tigeot * drm_format_num_planes - get the number of planes for format 3809b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 3810b5162e19SFrançois Tigeot * 3811b5162e19SFrançois Tigeot * RETURNS: 3812b5162e19SFrançois Tigeot * The number of planes used by the specified pixel format. 3813b5162e19SFrançois Tigeot */ 3814b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format) 3815b5162e19SFrançois Tigeot { 3816b5162e19SFrançois Tigeot switch (format) { 3817b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 3818b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 3819b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 3820b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 3821b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 3822b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 3823b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 3824b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 3825b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 3826b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 3827b5162e19SFrançois Tigeot return 3; 3828b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 3829b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 3830b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 3831b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 3832b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 3833b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 3834b5162e19SFrançois Tigeot return 2; 3835b5162e19SFrançois Tigeot default: 3836b5162e19SFrançois Tigeot return 1; 3837b5162e19SFrançois Tigeot } 3838b5162e19SFrançois Tigeot } 3839b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes); 3840b5162e19SFrançois Tigeot 3841b5162e19SFrançois Tigeot /** 3842b5162e19SFrançois Tigeot * drm_format_plane_cpp - determine the bytes per pixel value 3843b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 3844b5162e19SFrançois Tigeot * @plane: plane index 3845b5162e19SFrançois Tigeot * 3846b5162e19SFrançois Tigeot * RETURNS: 3847b5162e19SFrançois Tigeot * The bytes per pixel value for the specified plane. 3848b5162e19SFrançois Tigeot */ 3849b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane) 3850b5162e19SFrançois Tigeot { 3851b5162e19SFrançois Tigeot unsigned int depth; 3852b5162e19SFrançois Tigeot int bpp; 3853b5162e19SFrançois Tigeot 3854b5162e19SFrançois Tigeot if (plane >= drm_format_num_planes(format)) 3855b5162e19SFrançois Tigeot return 0; 3856b5162e19SFrançois Tigeot 3857b5162e19SFrançois Tigeot switch (format) { 3858b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 3859b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 3860b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 3861b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 3862b5162e19SFrançois Tigeot return 2; 3863b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 3864b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 3865b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 3866b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 3867b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 3868b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 3869b5162e19SFrançois Tigeot return plane ? 2 : 1; 3870b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 3871b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 3872b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 3873b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 3874b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 3875b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 3876b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 3877b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 3878b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 3879b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 3880b5162e19SFrançois Tigeot return 1; 3881b5162e19SFrançois Tigeot default: 3882b5162e19SFrançois Tigeot drm_fb_get_bpp_depth(format, &depth, &bpp); 3883b5162e19SFrançois Tigeot return bpp >> 3; 3884b5162e19SFrançois Tigeot } 3885b5162e19SFrançois Tigeot } 3886b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp); 3887b5162e19SFrançois Tigeot 3888b5162e19SFrançois Tigeot /** 3889b5162e19SFrançois Tigeot * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor 3890b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 3891b5162e19SFrançois Tigeot * 3892b5162e19SFrançois Tigeot * RETURNS: 3893b5162e19SFrançois Tigeot * The horizontal chroma subsampling factor for the 3894b5162e19SFrançois Tigeot * specified pixel format. 3895b5162e19SFrançois Tigeot */ 3896b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format) 3897b5162e19SFrançois Tigeot { 3898b5162e19SFrançois Tigeot switch (format) { 3899b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 3900b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 3901b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 3902b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 3903b5162e19SFrançois Tigeot return 4; 3904b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 3905b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 3906b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 3907b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 3908b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 3909b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 3910b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 3911b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 3912b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 3913b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 3914b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 3915b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 3916b5162e19SFrançois Tigeot return 2; 3917b5162e19SFrançois Tigeot default: 3918b5162e19SFrançois Tigeot return 1; 3919b5162e19SFrançois Tigeot } 3920b5162e19SFrançois Tigeot } 3921b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); 3922b5162e19SFrançois Tigeot 3923b5162e19SFrançois Tigeot /** 3924b5162e19SFrançois Tigeot * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor 3925b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 3926b5162e19SFrançois Tigeot * 3927b5162e19SFrançois Tigeot * RETURNS: 3928b5162e19SFrançois Tigeot * The vertical chroma subsampling factor for the 3929b5162e19SFrançois Tigeot * specified pixel format. 3930b5162e19SFrançois Tigeot */ 3931b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format) 3932b5162e19SFrançois Tigeot { 3933b5162e19SFrançois Tigeot switch (format) { 3934b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 3935b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 3936b5162e19SFrançois Tigeot return 4; 3937b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 3938b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 3939b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 3940b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 3941b5162e19SFrançois Tigeot return 2; 3942b5162e19SFrançois Tigeot default: 3943b5162e19SFrançois Tigeot return 1; 3944b5162e19SFrançois Tigeot } 3945b5162e19SFrançois Tigeot } 3946b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); 39474dbb207bSFrançois Tigeot 39484dbb207bSFrançois Tigeot /** 39494dbb207bSFrançois Tigeot * drm_mode_config_init - initialize DRM mode_configuration structure 39504dbb207bSFrançois Tigeot * @dev: DRM device 39514dbb207bSFrançois Tigeot * 39524dbb207bSFrançois Tigeot * Initialize @dev's mode_config structure, used for tracking the graphics 39534dbb207bSFrançois Tigeot * configuration of @dev. 39544dbb207bSFrançois Tigeot * 39554dbb207bSFrançois Tigeot * Since this initializes the modeset locks, no locking is possible. Which is no 39564dbb207bSFrançois Tigeot * problem, since this should happen single threaded at init time. It is the 39574dbb207bSFrançois Tigeot * driver's problem to ensure this guarantee. 39584dbb207bSFrançois Tigeot * 39594dbb207bSFrançois Tigeot */ 39604dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev) 39614dbb207bSFrançois Tigeot { 39624dbb207bSFrançois Tigeot lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE); 39634dbb207bSFrançois Tigeot lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE); 39644dbb207bSFrançois Tigeot lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE); 39654dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.fb_list); 39664dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.crtc_list); 39674dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.connector_list); 39684dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.encoder_list); 39694dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_list); 39704dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_blob_list); 39714dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.plane_list); 39724dbb207bSFrançois Tigeot idr_init(&dev->mode_config.crtc_idr); 39734dbb207bSFrançois Tigeot 39744dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 39754dbb207bSFrançois Tigeot drm_mode_create_standard_connector_properties(dev); 39764dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 39774dbb207bSFrançois Tigeot 39784dbb207bSFrançois Tigeot /* Just to be sure */ 39794dbb207bSFrançois Tigeot dev->mode_config.num_fb = 0; 39804dbb207bSFrançois Tigeot dev->mode_config.num_connector = 0; 39814dbb207bSFrançois Tigeot dev->mode_config.num_crtc = 0; 39824dbb207bSFrançois Tigeot dev->mode_config.num_encoder = 0; 39834dbb207bSFrançois Tigeot } 39844dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init); 39854dbb207bSFrançois Tigeot 39864dbb207bSFrançois Tigeot /** 39874dbb207bSFrançois Tigeot * drm_mode_config_cleanup - free up DRM mode_config info 39884dbb207bSFrançois Tigeot * @dev: DRM device 39894dbb207bSFrançois Tigeot * 39904dbb207bSFrançois Tigeot * Free up all the connectors and CRTCs associated with this DRM device, then 39914dbb207bSFrançois Tigeot * free up the framebuffers and associated buffer objects. 39924dbb207bSFrançois Tigeot * 39934dbb207bSFrançois Tigeot * Note that since this /should/ happen single-threaded at driver/device 39944dbb207bSFrançois Tigeot * teardown time, no locking is required. It's the driver's job to ensure that 39954dbb207bSFrançois Tigeot * this guarantee actually holds true. 39964dbb207bSFrançois Tigeot * 39974dbb207bSFrançois Tigeot * FIXME: cleanup any dangling user buffer objects too 39984dbb207bSFrançois Tigeot */ 39994dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev) 40004dbb207bSFrançois Tigeot { 40014dbb207bSFrançois Tigeot struct drm_connector *connector, *ot; 40024dbb207bSFrançois Tigeot struct drm_crtc *crtc, *ct; 40034dbb207bSFrançois Tigeot struct drm_encoder *encoder, *enct; 40044dbb207bSFrançois Tigeot struct drm_framebuffer *fb, *fbt; 40054dbb207bSFrançois Tigeot struct drm_property *property, *pt; 40064dbb207bSFrançois Tigeot struct drm_property_blob *blob, *bt; 40074dbb207bSFrançois Tigeot struct drm_plane *plane, *plt; 40084dbb207bSFrançois Tigeot 40094dbb207bSFrançois Tigeot list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, 40104dbb207bSFrançois Tigeot head) { 40114dbb207bSFrançois Tigeot encoder->funcs->destroy(encoder); 40124dbb207bSFrançois Tigeot } 40134dbb207bSFrançois Tigeot 40144dbb207bSFrançois Tigeot list_for_each_entry_safe(connector, ot, 40154dbb207bSFrançois Tigeot &dev->mode_config.connector_list, head) { 40164dbb207bSFrançois Tigeot connector->funcs->destroy(connector); 40174dbb207bSFrançois Tigeot } 40184dbb207bSFrançois Tigeot 40194dbb207bSFrançois Tigeot list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, 40204dbb207bSFrançois Tigeot head) { 40214dbb207bSFrançois Tigeot drm_property_destroy(dev, property); 40224dbb207bSFrançois Tigeot } 40234dbb207bSFrançois Tigeot 40244dbb207bSFrançois Tigeot list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, 40254dbb207bSFrançois Tigeot head) { 40264dbb207bSFrançois Tigeot drm_property_destroy_blob(dev, blob); 40274dbb207bSFrançois Tigeot } 40284dbb207bSFrançois Tigeot 40294dbb207bSFrançois Tigeot /* 40304dbb207bSFrançois Tigeot * Single-threaded teardown context, so it's not required to grab the 40314dbb207bSFrançois Tigeot * fb_lock to protect against concurrent fb_list access. Contrary, it 40324dbb207bSFrançois Tigeot * would actually deadlock with the drm_framebuffer_cleanup function. 40334dbb207bSFrançois Tigeot * 40344dbb207bSFrançois Tigeot * Also, if there are any framebuffers left, that's a driver leak now, 40354dbb207bSFrançois Tigeot * so politely WARN about this. 40364dbb207bSFrançois Tigeot */ 40374dbb207bSFrançois Tigeot WARN_ON(!list_empty(&dev->mode_config.fb_list)); 40384dbb207bSFrançois Tigeot list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 40394dbb207bSFrançois Tigeot drm_framebuffer_remove(fb); 40404dbb207bSFrançois Tigeot } 40414dbb207bSFrançois Tigeot 40424dbb207bSFrançois Tigeot list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 40434dbb207bSFrançois Tigeot head) { 40444dbb207bSFrançois Tigeot plane->funcs->destroy(plane); 40454dbb207bSFrançois Tigeot } 40464dbb207bSFrançois Tigeot 40474dbb207bSFrançois Tigeot list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { 40484dbb207bSFrançois Tigeot crtc->funcs->destroy(crtc); 40494dbb207bSFrançois Tigeot } 40504dbb207bSFrançois Tigeot 40514dbb207bSFrançois Tigeot idr_destroy(&dev->mode_config.crtc_idr); 40524dbb207bSFrançois Tigeot } 40534dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup); 4054