15718399fSFrançois Tigeot /* 25718399fSFrançois Tigeot * Copyright (c) 2006-2008 Intel Corporation 35718399fSFrançois Tigeot * Copyright (c) 2007 Dave Airlie <airlied@linux.ie> 45718399fSFrançois Tigeot * Copyright (c) 2008 Red Hat Inc. 55718399fSFrançois Tigeot * 65718399fSFrançois Tigeot * DRM core CRTC related functions 75718399fSFrançois Tigeot * 85718399fSFrançois Tigeot * Permission to use, copy, modify, distribute, and sell this software and its 95718399fSFrançois Tigeot * documentation for any purpose is hereby granted without fee, provided that 105718399fSFrançois Tigeot * the above copyright notice appear in all copies and that both that copyright 115718399fSFrançois Tigeot * notice and this permission notice appear in supporting documentation, and 125718399fSFrançois Tigeot * that the name of the copyright holders not be used in advertising or 135718399fSFrançois Tigeot * publicity pertaining to distribution of the software without specific, 145718399fSFrançois Tigeot * written prior permission. The copyright holders make no representations 155718399fSFrançois Tigeot * about the suitability of this software for any purpose. It is provided "as 165718399fSFrançois Tigeot * is" without express or implied warranty. 175718399fSFrançois Tigeot * 185718399fSFrançois Tigeot * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 195718399fSFrançois Tigeot * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO 205718399fSFrançois Tigeot * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR 215718399fSFrançois Tigeot * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, 225718399fSFrançois Tigeot * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 235718399fSFrançois Tigeot * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 245718399fSFrançois Tigeot * OF THIS SOFTWARE. 255718399fSFrançois Tigeot * 265718399fSFrançois Tigeot * Authors: 275718399fSFrançois Tigeot * Keith Packard 285718399fSFrançois Tigeot * Eric Anholt <eric@anholt.net> 295718399fSFrançois Tigeot * Dave Airlie <airlied@linux.ie> 305718399fSFrançois Tigeot * Jesse Barnes <jesse.barnes@intel.com> 315718399fSFrançois Tigeot */ 3206fede5aSFrançois Tigeot #include <linux/ctype.h> 33b5162e19SFrançois Tigeot #include <linux/list.h> 34b5162e19SFrançois Tigeot #include <linux/export.h> 3518e26a6dSFrançois Tigeot #include <drm/drmP.h> 3618e26a6dSFrançois Tigeot #include <drm/drm_crtc.h> 3718e26a6dSFrançois Tigeot #include <drm/drm_edid.h> 38*83b4b9b9SFrançois Tigeot #include <drm/drm_fourcc.h> 39ba55f2f5SFrançois Tigeot #include <linux/slab.h> 40ba55f2f5SFrançois Tigeot #include <drm/drm_modeset_lock.h> 412c9916cdSFrançois Tigeot #include <drm/drm_atomic.h> 42ba55f2f5SFrançois Tigeot 43ba55f2f5SFrançois Tigeot #include "drm_crtc_internal.h" 442c9916cdSFrançois Tigeot #include "drm_internal.h" 455718399fSFrançois Tigeot 462c9916cdSFrançois Tigeot static struct drm_framebuffer * 472c9916cdSFrançois Tigeot internal_framebuffer_create(struct drm_device *dev, 48aee94f86SFrançois Tigeot const struct drm_mode_fb_cmd2 *r, 4924edb884SFrançois Tigeot struct drm_file *file_priv); 5024edb884SFrançois Tigeot 515718399fSFrançois Tigeot /* Avoid boilerplate. I'm tired of typing. */ 525718399fSFrançois Tigeot #define DRM_ENUM_NAME_FN(fnname, list) \ 5306fede5aSFrançois Tigeot const char *fnname(int val) \ 545718399fSFrançois Tigeot { \ 555718399fSFrançois Tigeot int i; \ 56b5162e19SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(list); i++) { \ 575718399fSFrançois Tigeot if (list[i].type == val) \ 585718399fSFrançois Tigeot return list[i].name; \ 595718399fSFrançois Tigeot } \ 605718399fSFrançois Tigeot return "(unknown)"; \ 615718399fSFrançois Tigeot } 625718399fSFrançois Tigeot 635718399fSFrançois Tigeot /* 645718399fSFrançois Tigeot * Global properties 655718399fSFrançois Tigeot */ 662c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dpms_enum_list[] = { 672c9916cdSFrançois Tigeot { DRM_MODE_DPMS_ON, "On" }, 685718399fSFrançois Tigeot { DRM_MODE_DPMS_STANDBY, "Standby" }, 695718399fSFrançois Tigeot { DRM_MODE_DPMS_SUSPEND, "Suspend" }, 705718399fSFrançois Tigeot { DRM_MODE_DPMS_OFF, "Off" } 715718399fSFrançois Tigeot }; 725718399fSFrançois Tigeot 735718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dpms_name, drm_dpms_enum_list) 745718399fSFrançois Tigeot 752c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_plane_type_enum_list[] = { 76ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_OVERLAY, "Overlay" }, 77ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_PRIMARY, "Primary" }, 78ba55f2f5SFrançois Tigeot { DRM_PLANE_TYPE_CURSOR, "Cursor" }, 79ba55f2f5SFrançois Tigeot }; 80ba55f2f5SFrançois Tigeot 815718399fSFrançois Tigeot /* 825718399fSFrançois Tigeot * Optional properties 835718399fSFrançois Tigeot */ 842c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_scaling_mode_enum_list[] = { 855718399fSFrançois Tigeot { DRM_MODE_SCALE_NONE, "None" }, 865718399fSFrançois Tigeot { DRM_MODE_SCALE_FULLSCREEN, "Full" }, 875718399fSFrançois Tigeot { DRM_MODE_SCALE_CENTER, "Center" }, 885718399fSFrançois Tigeot { DRM_MODE_SCALE_ASPECT, "Full aspect" }, 895718399fSFrançois Tigeot }; 905718399fSFrançois Tigeot 9124edb884SFrançois Tigeot static const struct drm_prop_enum_list drm_aspect_ratio_enum_list[] = { 9224edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_NONE, "Automatic" }, 9324edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_4_3, "4:3" }, 9424edb884SFrançois Tigeot { DRM_MODE_PICTURE_ASPECT_16_9, "16:9" }, 9524edb884SFrançois Tigeot }; 9624edb884SFrançois Tigeot 975718399fSFrançois Tigeot /* 985718399fSFrançois Tigeot * Non-global properties, but "required" for certain connectors. 995718399fSFrançois Tigeot */ 1002c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_select_enum_list[] = { 1015718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 1025718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 1035718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 1045718399fSFrançois Tigeot }; 1055718399fSFrançois Tigeot 1065718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_select_name, drm_dvi_i_select_enum_list) 1075718399fSFrançois Tigeot 1082c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_dvi_i_subconnector_enum_list[] = { 1095718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 1105718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVID, "DVI-D" }, /* DVI-I */ 1115718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_DVIA, "DVI-A" }, /* DVI-I */ 1125718399fSFrançois Tigeot }; 1135718399fSFrançois Tigeot 1145718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_dvi_i_subconnector_name, 1155718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list) 1165718399fSFrançois Tigeot 1172c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_select_enum_list[] = { 1185718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Automatic, "Automatic" }, /* DVI-I and TV-out */ 1195718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 1205718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 1215718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 1225718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 1235718399fSFrançois Tigeot }; 1245718399fSFrançois Tigeot 1255718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_select_name, drm_tv_select_enum_list) 1265718399fSFrançois Tigeot 1272c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_tv_subconnector_enum_list[] = { 1285718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Unknown, "Unknown" }, /* DVI-I and TV-out */ 1295718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Composite, "Composite" }, /* TV-out */ 1305718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SVIDEO, "SVIDEO" }, /* TV-out */ 1315718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_Component, "Component" }, /* TV-out */ 1325718399fSFrançois Tigeot { DRM_MODE_SUBCONNECTOR_SCART, "SCART" }, /* TV-out */ 1335718399fSFrançois Tigeot }; 1345718399fSFrançois Tigeot 1355718399fSFrançois Tigeot DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name, 1365718399fSFrançois Tigeot drm_tv_subconnector_enum_list) 1375718399fSFrançois Tigeot 13806fede5aSFrançois Tigeot static const struct drm_prop_enum_list drm_dirty_info_enum_list[] = { 1395718399fSFrançois Tigeot { DRM_MODE_DIRTY_OFF, "Off" }, 1405718399fSFrançois Tigeot { DRM_MODE_DIRTY_ON, "On" }, 1415718399fSFrançois Tigeot { DRM_MODE_DIRTY_ANNOTATE, "Annotate" }, 1425718399fSFrançois Tigeot }; 1435718399fSFrançois Tigeot 1445718399fSFrançois Tigeot struct drm_conn_prop_enum_list { 1455718399fSFrançois Tigeot int type; 14606fede5aSFrançois Tigeot const char *name; 147917ec290SFrançois Tigeot struct ida ida; 148c7dca6a7SFrançois Tigeot int count; 1495718399fSFrançois Tigeot }; 1505718399fSFrançois Tigeot 1515718399fSFrançois Tigeot /* 1525718399fSFrançois Tigeot * Connector and encoder types. 1535718399fSFrançois Tigeot */ 1542c9916cdSFrançois Tigeot static struct drm_conn_prop_enum_list drm_connector_enum_list[] = { 1552c9916cdSFrançois Tigeot { DRM_MODE_CONNECTOR_Unknown, "Unknown" }, 1569edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VGA, "VGA" }, 1579edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVII, "DVI-I" }, 1589edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVID, "DVI-D" }, 1599edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DVIA, "DVI-A" }, 1609edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Composite, "Composite" }, 1619edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_SVIDEO, "SVIDEO" }, 1629edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_LVDS, "LVDS" }, 1639edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_Component, "Component" }, 1649edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_9PinDIN, "DIN" }, 1659edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DisplayPort, "DP" }, 1669edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIA, "HDMI-A" }, 1679edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_HDMIB, "HDMI-B" }, 1689edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_TV, "TV" }, 1699edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_eDP, "eDP" }, 1709edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_VIRTUAL, "Virtual" }, 1719edbd4a0SFrançois Tigeot { DRM_MODE_CONNECTOR_DSI, "DSI" }, 1728621f407SFrançois Tigeot { DRM_MODE_CONNECTOR_DPI, "DPI" }, 1735718399fSFrançois Tigeot }; 1745718399fSFrançois Tigeot 1752c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_encoder_enum_list[] = { 1762c9916cdSFrançois Tigeot { DRM_MODE_ENCODER_NONE, "None" }, 1775718399fSFrançois Tigeot { DRM_MODE_ENCODER_DAC, "DAC" }, 1785718399fSFrançois Tigeot { DRM_MODE_ENCODER_TMDS, "TMDS" }, 1795718399fSFrançois Tigeot { DRM_MODE_ENCODER_LVDS, "LVDS" }, 1805718399fSFrançois Tigeot { DRM_MODE_ENCODER_TVDAC, "TV" }, 181b5162e19SFrançois Tigeot { DRM_MODE_ENCODER_VIRTUAL, "Virtual" }, 1829edbd4a0SFrançois Tigeot { DRM_MODE_ENCODER_DSI, "DSI" }, 183ba55f2f5SFrançois Tigeot { DRM_MODE_ENCODER_DPMST, "DP MST" }, 1848621f407SFrançois Tigeot { DRM_MODE_ENCODER_DPI, "DPI" }, 1855718399fSFrançois Tigeot }; 1865718399fSFrançois Tigeot 1872c9916cdSFrançois Tigeot static const struct drm_prop_enum_list drm_subpixel_enum_list[] = { 188ba55f2f5SFrançois Tigeot { SubPixelUnknown, "Unknown" }, 189ba55f2f5SFrançois Tigeot { SubPixelHorizontalRGB, "Horizontal RGB" }, 190ba55f2f5SFrançois Tigeot { SubPixelHorizontalBGR, "Horizontal BGR" }, 191ba55f2f5SFrançois Tigeot { SubPixelVerticalRGB, "Vertical RGB" }, 192ba55f2f5SFrançois Tigeot { SubPixelVerticalBGR, "Vertical BGR" }, 193ba55f2f5SFrançois Tigeot { SubPixelNone, "None" }, 194ba55f2f5SFrançois Tigeot }; 1955718399fSFrançois Tigeot 196ba55f2f5SFrançois Tigeot void drm_connector_ida_init(void) 1975718399fSFrançois Tigeot { 198ba55f2f5SFrançois Tigeot int i; 1995718399fSFrançois Tigeot 200ba55f2f5SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 201ba55f2f5SFrançois Tigeot ida_init(&drm_connector_enum_list[i].ida); 2025718399fSFrançois Tigeot } 2035718399fSFrançois Tigeot 204ba55f2f5SFrançois Tigeot void drm_connector_ida_destroy(void) 205ba55f2f5SFrançois Tigeot { 206ba55f2f5SFrançois Tigeot int i; 207ba55f2f5SFrançois Tigeot 208ba55f2f5SFrançois Tigeot for (i = 0; i < ARRAY_SIZE(drm_connector_enum_list); i++) 209ba55f2f5SFrançois Tigeot ida_destroy(&drm_connector_enum_list[i].ida); 210ba55f2f5SFrançois Tigeot } 211ba55f2f5SFrançois Tigeot 212ba55f2f5SFrançois Tigeot /** 213ba55f2f5SFrançois Tigeot * drm_get_connector_status_name - return a string for connector status 214ba55f2f5SFrançois Tigeot * @status: connector status to compute name of 215ba55f2f5SFrançois Tigeot * 216ba55f2f5SFrançois Tigeot * In contrast to the other drm_get_*_name functions this one here returns a 217ba55f2f5SFrançois Tigeot * const pointer and hence is threadsafe. 218ba55f2f5SFrançois Tigeot */ 21906fede5aSFrançois Tigeot const char *drm_get_connector_status_name(enum drm_connector_status status) 2205718399fSFrançois Tigeot { 2215718399fSFrançois Tigeot if (status == connector_status_connected) 2225718399fSFrançois Tigeot return "connected"; 2235718399fSFrançois Tigeot else if (status == connector_status_disconnected) 2245718399fSFrançois Tigeot return "disconnected"; 2255718399fSFrançois Tigeot else 2265718399fSFrançois Tigeot return "unknown"; 2275718399fSFrançois Tigeot } 2284dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_get_connector_status_name); 2295718399fSFrançois Tigeot 230ba55f2f5SFrançois Tigeot /** 231ba55f2f5SFrançois Tigeot * drm_get_subpixel_order_name - return a string for a given subpixel enum 232ba55f2f5SFrançois Tigeot * @order: enum of subpixel_order 233ba55f2f5SFrançois Tigeot * 234ba55f2f5SFrançois Tigeot * Note you could abuse this and return something out of bounds, but that 235ba55f2f5SFrançois Tigeot * would be a caller error. No unscrubbed user data should make it here. 236ba55f2f5SFrançois Tigeot */ 237ba55f2f5SFrançois Tigeot const char *drm_get_subpixel_order_name(enum subpixel_order order) 238ba55f2f5SFrançois Tigeot { 239ba55f2f5SFrançois Tigeot return drm_subpixel_enum_list[order].name; 240ba55f2f5SFrançois Tigeot } 241ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_get_subpixel_order_name); 242ba55f2f5SFrançois Tigeot 24306fede5aSFrançois Tigeot static char printable_char(int c) 24406fede5aSFrançois Tigeot { 24506fede5aSFrançois Tigeot return isascii(c) && isprint(c) ? c : '?'; 24606fede5aSFrançois Tigeot } 24706fede5aSFrançois Tigeot 248ba55f2f5SFrançois Tigeot /** 249ba55f2f5SFrançois Tigeot * drm_get_format_name - return a string for drm fourcc format 250ba55f2f5SFrançois Tigeot * @format: format to compute name of 251ba55f2f5SFrançois Tigeot * 252ba55f2f5SFrançois Tigeot * Note that the buffer used by this function is globally shared and owned by 253ba55f2f5SFrançois Tigeot * the function itself. 254ba55f2f5SFrançois Tigeot * 255ba55f2f5SFrançois Tigeot * FIXME: This isn't really multithreading safe. 256ba55f2f5SFrançois Tigeot */ 25706fede5aSFrançois Tigeot const char *drm_get_format_name(uint32_t format) 25806fede5aSFrançois Tigeot { 25906fede5aSFrançois Tigeot static char buf[32]; 26006fede5aSFrançois Tigeot 26106fede5aSFrançois Tigeot ksnprintf(buf, sizeof(buf), 26206fede5aSFrançois Tigeot "%c%c%c%c %s-endian (0x%08x)", 26306fede5aSFrançois Tigeot printable_char(format & 0xff), 26406fede5aSFrançois Tigeot printable_char((format >> 8) & 0xff), 26506fede5aSFrançois Tigeot printable_char((format >> 16) & 0xff), 26606fede5aSFrançois Tigeot printable_char((format >> 24) & 0x7f), 26706fede5aSFrançois Tigeot format & DRM_FORMAT_BIG_ENDIAN ? "big" : "little", 26806fede5aSFrançois Tigeot format); 26906fede5aSFrançois Tigeot 27006fede5aSFrançois Tigeot return buf; 27106fede5aSFrançois Tigeot } 27206fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_get_format_name); 27306fede5aSFrançois Tigeot 2742c9916cdSFrançois Tigeot /* 2752c9916cdSFrançois Tigeot * Internal function to assign a slot in the object idr and optionally 2762c9916cdSFrançois Tigeot * register the object into the idr. 2772c9916cdSFrançois Tigeot */ 2782c9916cdSFrançois Tigeot static int drm_mode_object_get_reg(struct drm_device *dev, 2792c9916cdSFrançois Tigeot struct drm_mode_object *obj, 2802c9916cdSFrançois Tigeot uint32_t obj_type, 2818621f407SFrançois Tigeot bool register_obj, 2828621f407SFrançois Tigeot void (*obj_free_cb)(struct kref *kref)) 2832c9916cdSFrançois Tigeot { 2842c9916cdSFrançois Tigeot int ret; 2852c9916cdSFrançois Tigeot 2862c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 2872c9916cdSFrançois Tigeot ret = idr_alloc(&dev->mode_config.crtc_idr, register_obj ? obj : NULL, 1, 0, GFP_KERNEL); 2882c9916cdSFrançois Tigeot if (ret >= 0) { 2892c9916cdSFrançois Tigeot /* 2902c9916cdSFrançois Tigeot * Set up the object linking under the protection of the idr 2912c9916cdSFrançois Tigeot * lock so that other users can't see inconsistent state. 2922c9916cdSFrançois Tigeot */ 2932c9916cdSFrançois Tigeot obj->id = ret; 2942c9916cdSFrançois Tigeot obj->type = obj_type; 2958621f407SFrançois Tigeot if (obj_free_cb) { 2968621f407SFrançois Tigeot obj->free_cb = obj_free_cb; 2978621f407SFrançois Tigeot kref_init(&obj->refcount); 2988621f407SFrançois Tigeot } 2992c9916cdSFrançois Tigeot } 3002c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3012c9916cdSFrançois Tigeot 3022c9916cdSFrançois Tigeot return ret < 0 ? ret : 0; 3032c9916cdSFrançois Tigeot } 3042c9916cdSFrançois Tigeot 3055718399fSFrançois Tigeot /** 3064dbb207bSFrançois Tigeot * drm_mode_object_get - allocate a new modeset identifier 3075718399fSFrançois Tigeot * @dev: DRM device 3084dbb207bSFrançois Tigeot * @obj: object pointer, used to generate unique ID 3094dbb207bSFrançois Tigeot * @obj_type: object type 3105718399fSFrançois Tigeot * 3115718399fSFrançois Tigeot * Create a unique identifier based on @ptr in @dev's identifier space. Used 312ba55f2f5SFrançois Tigeot * for tracking modes, CRTCs and connectors. Note that despite the _get postfix 313ba55f2f5SFrançois Tigeot * modeset identifiers are _not_ reference counted. Hence don't use this for 314ba55f2f5SFrançois Tigeot * reference counted modeset objects like framebuffers. 3155718399fSFrançois Tigeot * 316ba55f2f5SFrançois Tigeot * Returns: 317352ff8bdSFrançois Tigeot * Zero on success, error code on failure. 3185718399fSFrançois Tigeot */ 319ba55f2f5SFrançois Tigeot int drm_mode_object_get(struct drm_device *dev, 3205718399fSFrançois Tigeot struct drm_mode_object *obj, uint32_t obj_type) 3215718399fSFrançois Tigeot { 3228621f407SFrançois Tigeot return drm_mode_object_get_reg(dev, obj, obj_type, true, NULL); 3239f0f5970SFrançois Tigeot } 3249f0f5970SFrançois Tigeot 3252c9916cdSFrançois Tigeot static void drm_mode_object_register(struct drm_device *dev, 3262c9916cdSFrançois Tigeot struct drm_mode_object *obj) 3272c9916cdSFrançois Tigeot { 3282c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 3292c9916cdSFrançois Tigeot idr_replace(&dev->mode_config.crtc_idr, obj, obj->id); 3302c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3315718399fSFrançois Tigeot } 3325718399fSFrançois Tigeot 3335718399fSFrançois Tigeot /** 3348621f407SFrançois Tigeot * drm_mode_object_unregister - free a modeset identifer 3355718399fSFrançois Tigeot * @dev: DRM device 3364dbb207bSFrançois Tigeot * @object: object to free 3375718399fSFrançois Tigeot * 3388621f407SFrançois Tigeot * Free @id from @dev's unique identifier pool. 3398621f407SFrançois Tigeot * This function can be called multiple times, and guards against 3408621f407SFrançois Tigeot * multiple removals. 3418621f407SFrançois Tigeot * These modeset identifiers are _not_ reference counted. Hence don't use this 342ba55f2f5SFrançois Tigeot * for reference counted modeset objects like framebuffers. 3435718399fSFrançois Tigeot */ 3448621f407SFrançois Tigeot void drm_mode_object_unregister(struct drm_device *dev, 3455718399fSFrançois Tigeot struct drm_mode_object *object) 3465718399fSFrançois Tigeot { 3474dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 3488621f407SFrançois Tigeot if (object->id) { 349aea8bdbdSFrançois Tigeot idr_remove(&dev->mode_config.crtc_idr, object->id); 3508621f407SFrançois Tigeot object->id = 0; 3518621f407SFrançois Tigeot } 3524dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 3535718399fSFrançois Tigeot } 3545718399fSFrançois Tigeot 355ba55f2f5SFrançois Tigeot static struct drm_mode_object *_object_find(struct drm_device *dev, 356ba55f2f5SFrançois Tigeot uint32_t id, uint32_t type) 357ba55f2f5SFrançois Tigeot { 358ba55f2f5SFrançois Tigeot struct drm_mode_object *obj = NULL; 359ba55f2f5SFrançois Tigeot 360ba55f2f5SFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 361ba55f2f5SFrançois Tigeot obj = idr_find(&dev->mode_config.crtc_idr, id); 36224edb884SFrançois Tigeot if (obj && type != DRM_MODE_OBJECT_ANY && obj->type != type) 36324edb884SFrançois Tigeot obj = NULL; 36424edb884SFrançois Tigeot if (obj && obj->id != id) 36524edb884SFrançois Tigeot obj = NULL; 3668621f407SFrançois Tigeot 3678621f407SFrançois Tigeot if (obj && obj->free_cb) { 3688621f407SFrançois Tigeot if (!kref_get_unless_zero(&obj->refcount)) 369ba55f2f5SFrançois Tigeot obj = NULL; 3708621f407SFrançois Tigeot } 371ba55f2f5SFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 372ba55f2f5SFrançois Tigeot 373ba55f2f5SFrançois Tigeot return obj; 374ba55f2f5SFrançois Tigeot } 375ba55f2f5SFrançois Tigeot 3764dbb207bSFrançois Tigeot /** 3774dbb207bSFrançois Tigeot * drm_mode_object_find - look up a drm object with static lifetime 3784dbb207bSFrançois Tigeot * @dev: drm device 3794dbb207bSFrançois Tigeot * @id: id of the mode object 3804dbb207bSFrançois Tigeot * @type: type of the mode object 3814dbb207bSFrançois Tigeot * 3828621f407SFrançois Tigeot * This function is used to look up a modeset object. It will acquire a 3838621f407SFrançois Tigeot * reference for reference counted objects. This reference must be dropped again 3848621f407SFrançois Tigeot * by callind drm_mode_object_unreference(). 3854dbb207bSFrançois Tigeot */ 3865718399fSFrançois Tigeot struct drm_mode_object *drm_mode_object_find(struct drm_device *dev, 3875718399fSFrançois Tigeot uint32_t id, uint32_t type) 3885718399fSFrançois Tigeot { 389aea8bdbdSFrançois Tigeot struct drm_mode_object *obj = NULL; 3905718399fSFrançois Tigeot 391ba55f2f5SFrançois Tigeot obj = _object_find(dev, id, type); 3925718399fSFrançois Tigeot return obj; 3935718399fSFrançois Tigeot } 394b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_find); 3955718399fSFrançois Tigeot 3965718399fSFrançois Tigeot /** 3978621f407SFrançois Tigeot * drm_mode_object_unreference - decr the object refcnt 3988621f407SFrançois Tigeot * @obj: mode_object 3998621f407SFrançois Tigeot * 4008621f407SFrançois Tigeot * This functions decrements the object's refcount if it is a refcounted modeset 4018621f407SFrançois Tigeot * object. It is a no-op on any other object. This is used to drop references 4028621f407SFrançois Tigeot * acquired with drm_mode_object_reference(). 4038621f407SFrançois Tigeot */ 4048621f407SFrançois Tigeot void drm_mode_object_unreference(struct drm_mode_object *obj) 4058621f407SFrançois Tigeot { 4068621f407SFrançois Tigeot if (obj->free_cb) { 4078621f407SFrançois Tigeot DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); 4088621f407SFrançois Tigeot kref_put(&obj->refcount, obj->free_cb); 4098621f407SFrançois Tigeot } 4108621f407SFrançois Tigeot } 4118621f407SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_unreference); 4128621f407SFrançois Tigeot 4138621f407SFrançois Tigeot /** 4148621f407SFrançois Tigeot * drm_mode_object_reference - incr the object refcnt 4158621f407SFrançois Tigeot * @obj: mode_object 4168621f407SFrançois Tigeot * 4178621f407SFrançois Tigeot * This functions increments the object's refcount if it is a refcounted modeset 4188621f407SFrançois Tigeot * object. It is a no-op on any other object. References should be dropped again 4198621f407SFrançois Tigeot * by calling drm_mode_object_unreference(). 4208621f407SFrançois Tigeot */ 4218621f407SFrançois Tigeot void drm_mode_object_reference(struct drm_mode_object *obj) 4228621f407SFrançois Tigeot { 4238621f407SFrançois Tigeot if (obj->free_cb) { 4248621f407SFrançois Tigeot DRM_DEBUG("OBJ ID: %d (%d)\n", obj->id, atomic_read(&obj->refcount.refcount)); 4258621f407SFrançois Tigeot kref_get(&obj->refcount); 4268621f407SFrançois Tigeot } 4278621f407SFrançois Tigeot } 4288621f407SFrançois Tigeot EXPORT_SYMBOL(drm_mode_object_reference); 4298621f407SFrançois Tigeot 4308621f407SFrançois Tigeot static void drm_framebuffer_free(struct kref *kref) 4318621f407SFrançois Tigeot { 4328621f407SFrançois Tigeot struct drm_framebuffer *fb = 4338621f407SFrançois Tigeot container_of(kref, struct drm_framebuffer, base.refcount); 4348621f407SFrançois Tigeot struct drm_device *dev = fb->dev; 4358621f407SFrançois Tigeot 4368621f407SFrançois Tigeot /* 4378621f407SFrançois Tigeot * The lookup idr holds a weak reference, which has not necessarily been 4388621f407SFrançois Tigeot * removed at this point. Check for that. 4398621f407SFrançois Tigeot */ 4408621f407SFrançois Tigeot drm_mode_object_unregister(dev, &fb->base); 4418621f407SFrançois Tigeot 4428621f407SFrançois Tigeot fb->funcs->destroy(fb); 4438621f407SFrançois Tigeot } 4448621f407SFrançois Tigeot 4458621f407SFrançois Tigeot /** 4465718399fSFrançois Tigeot * drm_framebuffer_init - initialize a framebuffer 4475718399fSFrançois Tigeot * @dev: DRM device 4484dbb207bSFrançois Tigeot * @fb: framebuffer to be initialized 4494dbb207bSFrançois Tigeot * @funcs: ... with these functions 4505718399fSFrançois Tigeot * 4515718399fSFrançois Tigeot * Allocates an ID for the framebuffer's parent mode object, sets its mode 4525718399fSFrançois Tigeot * functions & device file and adds it to the master fd list. 4535718399fSFrançois Tigeot * 4544dbb207bSFrançois Tigeot * IMPORTANT: 4554dbb207bSFrançois Tigeot * This functions publishes the fb and makes it available for concurrent access 4564dbb207bSFrançois Tigeot * by other users. Which means by this point the fb _must_ be fully set up - 4574dbb207bSFrançois Tigeot * since all the fb attributes are invariant over its lifetime, no further 4584dbb207bSFrançois Tigeot * locking but only correct reference counting is required. 4594dbb207bSFrançois Tigeot * 460ba55f2f5SFrançois Tigeot * Returns: 4615718399fSFrançois Tigeot * Zero on success, error code on failure. 4625718399fSFrançois Tigeot */ 4635718399fSFrançois Tigeot int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb, 4645718399fSFrançois Tigeot const struct drm_framebuffer_funcs *funcs) 4655718399fSFrançois Tigeot { 4665718399fSFrançois Tigeot int ret; 4675718399fSFrançois Tigeot 4684dbb207bSFrançois Tigeot INIT_LIST_HEAD(&fb->filp_head); 4694dbb207bSFrançois Tigeot fb->dev = dev; 4704dbb207bSFrançois Tigeot fb->funcs = funcs; 471b5162e19SFrançois Tigeot 4728621f407SFrançois Tigeot ret = drm_mode_object_get_reg(dev, &fb->base, DRM_MODE_OBJECT_FB, 4738621f407SFrançois Tigeot false, drm_framebuffer_free); 4745718399fSFrançois Tigeot if (ret) 4754dbb207bSFrançois Tigeot goto out; 4765718399fSFrançois Tigeot 4778621f407SFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 4785718399fSFrançois Tigeot dev->mode_config.num_fb++; 4795718399fSFrançois Tigeot list_add(&fb->head, &dev->mode_config.fb_list); 4804dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 4815718399fSFrançois Tigeot 4828621f407SFrançois Tigeot drm_mode_object_register(dev, &fb->base); 4838621f407SFrançois Tigeot out: 484352ff8bdSFrançois Tigeot return ret; 4855718399fSFrançois Tigeot } 486b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_init); 487b5162e19SFrançois Tigeot 4884dbb207bSFrançois Tigeot /** 4894dbb207bSFrançois Tigeot * drm_framebuffer_lookup - look up a drm framebuffer and grab a reference 4904dbb207bSFrançois Tigeot * @dev: drm device 4914dbb207bSFrançois Tigeot * @id: id of the fb object 4924dbb207bSFrançois Tigeot * 4934dbb207bSFrançois Tigeot * If successful, this grabs an additional reference to the framebuffer - 4944dbb207bSFrançois Tigeot * callers need to make sure to eventually unreference the returned framebuffer 495ba55f2f5SFrançois Tigeot * again, using @drm_framebuffer_unreference. 4964dbb207bSFrançois Tigeot */ 4974dbb207bSFrançois Tigeot struct drm_framebuffer *drm_framebuffer_lookup(struct drm_device *dev, 4984dbb207bSFrançois Tigeot uint32_t id) 4994dbb207bSFrançois Tigeot { 5008621f407SFrançois Tigeot struct drm_mode_object *obj; 5018621f407SFrançois Tigeot struct drm_framebuffer *fb = NULL; 5024dbb207bSFrançois Tigeot 5038621f407SFrançois Tigeot obj = _object_find(dev, id, DRM_MODE_OBJECT_FB); 5048621f407SFrançois Tigeot if (obj) 5058621f407SFrançois Tigeot fb = obj_to_fb(obj); 5064dbb207bSFrançois Tigeot return fb; 5074dbb207bSFrançois Tigeot } 5084dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_lookup); 5094dbb207bSFrançois Tigeot 510b5162e19SFrançois Tigeot /** 511a2fdbec6SFrançois Tigeot * drm_framebuffer_unregister_private - unregister a private fb from the lookup idr 512a2fdbec6SFrançois Tigeot * @fb: fb to unregister 513a2fdbec6SFrançois Tigeot * 514a2fdbec6SFrançois Tigeot * Drivers need to call this when cleaning up driver-private framebuffers, e.g. 515a2fdbec6SFrançois Tigeot * those used for fbdev. Note that the caller must hold a reference of it's own, 516a2fdbec6SFrançois Tigeot * i.e. the object may not be destroyed through this call (since it'll lead to a 517a2fdbec6SFrançois Tigeot * locking inversion). 518a2fdbec6SFrançois Tigeot */ 519a2fdbec6SFrançois Tigeot void drm_framebuffer_unregister_private(struct drm_framebuffer *fb) 520a2fdbec6SFrançois Tigeot { 521352ff8bdSFrançois Tigeot struct drm_device *dev; 522352ff8bdSFrançois Tigeot 523352ff8bdSFrançois Tigeot if (!fb) 524352ff8bdSFrançois Tigeot return; 525352ff8bdSFrançois Tigeot 526352ff8bdSFrançois Tigeot dev = fb->dev; 527a2fdbec6SFrançois Tigeot 528a2fdbec6SFrançois Tigeot /* Mark fb as reaped and drop idr ref. */ 5298621f407SFrançois Tigeot drm_mode_object_unregister(dev, &fb->base); 530a2fdbec6SFrançois Tigeot } 531a2fdbec6SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_unregister_private); 532a2fdbec6SFrançois Tigeot 5335718399fSFrançois Tigeot /** 5345718399fSFrançois Tigeot * drm_framebuffer_cleanup - remove a framebuffer object 5355718399fSFrançois Tigeot * @fb: framebuffer to remove 5365718399fSFrançois Tigeot * 537ba55f2f5SFrançois Tigeot * Cleanup framebuffer. This function is intended to be used from the drivers 538ba55f2f5SFrançois Tigeot * ->destroy callback. It can also be used to clean up driver private 539ba55f2f5SFrançois Tigeot * framebuffers embedded into a larger structure. 5405718399fSFrançois Tigeot * 5414dbb207bSFrançois Tigeot * Note that this function does not remove the fb from active usuage - if it is 5424dbb207bSFrançois Tigeot * still used anywhere, hilarity can ensue since userspace could call getfb on 5434dbb207bSFrançois Tigeot * the id and get back -EINVAL. Obviously no concern at driver unload time. 5444dbb207bSFrançois Tigeot * 5454dbb207bSFrançois Tigeot * Also, the framebuffer will not be removed from the lookup idr - for 5464dbb207bSFrançois Tigeot * user-created framebuffers this will happen in in the rmfb ioctl. For 5474dbb207bSFrançois Tigeot * driver-private objects (e.g. for fbdev) drivers need to explicitly call 5484dbb207bSFrançois Tigeot * drm_framebuffer_unregister_private. 5495718399fSFrançois Tigeot */ 5505718399fSFrançois Tigeot void drm_framebuffer_cleanup(struct drm_framebuffer *fb) 5515718399fSFrançois Tigeot { 5525718399fSFrançois Tigeot struct drm_device *dev = fb->dev; 5534dbb207bSFrançois Tigeot 5544dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.fb_lock); 555b5162e19SFrançois Tigeot list_del(&fb->head); 556b5162e19SFrançois Tigeot dev->mode_config.num_fb--; 5574dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.fb_lock); 558b5162e19SFrançois Tigeot } 559b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_cleanup); 560b5162e19SFrançois Tigeot 561b5162e19SFrançois Tigeot /** 562b5162e19SFrançois Tigeot * drm_framebuffer_remove - remove and unreference a framebuffer object 563b5162e19SFrançois Tigeot * @fb: framebuffer to remove 564b5162e19SFrançois Tigeot * 565b5162e19SFrançois Tigeot * Scans all the CRTCs and planes in @dev's mode_config. If they're 5664dbb207bSFrançois Tigeot * using @fb, removes it, setting it to NULL. Then drops the reference to the 5674dbb207bSFrançois Tigeot * passed-in framebuffer. Might take the modeset locks. 5684dbb207bSFrançois Tigeot * 5694dbb207bSFrançois Tigeot * Note that this function optimizes the cleanup away if the caller holds the 5704dbb207bSFrançois Tigeot * last reference to the framebuffer. It is also guaranteed to not take the 5714dbb207bSFrançois Tigeot * modeset locks in this case. 572b5162e19SFrançois Tigeot */ 573b5162e19SFrançois Tigeot void drm_framebuffer_remove(struct drm_framebuffer *fb) 574b5162e19SFrançois Tigeot { 575352ff8bdSFrançois Tigeot struct drm_device *dev; 5765718399fSFrançois Tigeot struct drm_crtc *crtc; 5775718399fSFrançois Tigeot struct drm_plane *plane; 5785718399fSFrançois Tigeot struct drm_mode_set set; 5795718399fSFrançois Tigeot int ret; 5805718399fSFrançois Tigeot 581352ff8bdSFrançois Tigeot if (!fb) 582352ff8bdSFrançois Tigeot return; 583352ff8bdSFrançois Tigeot 584352ff8bdSFrançois Tigeot dev = fb->dev; 585352ff8bdSFrançois Tigeot 5864dbb207bSFrançois Tigeot WARN_ON(!list_empty(&fb->filp_head)); 5874dbb207bSFrançois Tigeot 5884dbb207bSFrançois Tigeot /* 5894dbb207bSFrançois Tigeot * drm ABI mandates that we remove any deleted framebuffers from active 5904dbb207bSFrançois Tigeot * useage. But since most sane clients only remove framebuffers they no 5914dbb207bSFrançois Tigeot * longer need, try to optimize this away. 5924dbb207bSFrançois Tigeot * 5934dbb207bSFrançois Tigeot * Since we're holding a reference ourselves, observing a refcount of 1 5944dbb207bSFrançois Tigeot * means that we're the last holder and can skip it. Also, the refcount 5954dbb207bSFrançois Tigeot * can never increase from 1 again, so we don't need any barriers or 5964dbb207bSFrançois Tigeot * locks. 5974dbb207bSFrançois Tigeot * 5984dbb207bSFrançois Tigeot * Note that userspace could try to race with use and instate a new 5994dbb207bSFrançois Tigeot * usage _after_ we've cleared all current ones. End result will be an 6004dbb207bSFrançois Tigeot * in-use fb with fb-id == 0. Userspace is allowed to shoot its own foot 6014dbb207bSFrançois Tigeot * in this manner. 6024dbb207bSFrançois Tigeot */ 6038621f407SFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 6044dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 6055718399fSFrançois Tigeot /* remove from any CRTC */ 606a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) { 607ba55f2f5SFrançois Tigeot if (crtc->primary->fb == fb) { 6085718399fSFrançois Tigeot /* should turn off the crtc */ 6095718399fSFrançois Tigeot memset(&set, 0, sizeof(struct drm_mode_set)); 6105718399fSFrançois Tigeot set.crtc = crtc; 6115718399fSFrançois Tigeot set.fb = NULL; 6124dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 6135718399fSFrançois Tigeot if (ret) 6145718399fSFrançois Tigeot DRM_ERROR("failed to reset crtc %p when fb was deleted\n", crtc); 6155718399fSFrançois Tigeot } 6165718399fSFrançois Tigeot } 6175718399fSFrançois Tigeot 618a05eeebfSFrançois Tigeot drm_for_each_plane(plane, dev) { 61906fede5aSFrançois Tigeot if (plane->fb == fb) 62006fede5aSFrançois Tigeot drm_plane_force_disable(plane); 6215718399fSFrançois Tigeot } 6224dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 6234dbb207bSFrançois Tigeot } 624b5162e19SFrançois Tigeot 625b5162e19SFrançois Tigeot drm_framebuffer_unreference(fb); 6265718399fSFrançois Tigeot } 627b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_framebuffer_remove); 6285718399fSFrançois Tigeot 629ba55f2f5SFrançois Tigeot DEFINE_WW_CLASS(crtc_ww_class); 630ba55f2f5SFrançois Tigeot 631aee94f86SFrançois Tigeot static unsigned int drm_num_crtcs(struct drm_device *dev) 632aee94f86SFrançois Tigeot { 633aee94f86SFrançois Tigeot unsigned int num = 0; 634aee94f86SFrançois Tigeot struct drm_crtc *tmp; 635aee94f86SFrançois Tigeot 636aee94f86SFrançois Tigeot drm_for_each_crtc(tmp, dev) { 637aee94f86SFrançois Tigeot num++; 638aee94f86SFrançois Tigeot } 639aee94f86SFrançois Tigeot 640aee94f86SFrançois Tigeot return num; 641aee94f86SFrançois Tigeot } 642aee94f86SFrançois Tigeot 6435718399fSFrançois Tigeot /** 644ba55f2f5SFrançois Tigeot * drm_crtc_init_with_planes - Initialise a new CRTC object with 645ba55f2f5SFrançois Tigeot * specified primary and cursor planes. 6465718399fSFrançois Tigeot * @dev: DRM device 6475718399fSFrançois Tigeot * @crtc: CRTC object to init 648ba55f2f5SFrançois Tigeot * @primary: Primary plane for CRTC 649ba55f2f5SFrançois Tigeot * @cursor: Cursor plane for CRTC 6505718399fSFrançois Tigeot * @funcs: callbacks for the new CRTC 651aee94f86SFrançois Tigeot * @name: printf style format string for the CRTC name, or NULL for default name 6525718399fSFrançois Tigeot * 65306fede5aSFrançois Tigeot * Inits a new object created as base part of a driver crtc object. 6545718399fSFrançois Tigeot * 655ba55f2f5SFrançois Tigeot * Returns: 6565718399fSFrançois Tigeot * Zero on success, error code on failure. 6575718399fSFrançois Tigeot */ 658ba55f2f5SFrançois Tigeot int drm_crtc_init_with_planes(struct drm_device *dev, struct drm_crtc *crtc, 659ba55f2f5SFrançois Tigeot struct drm_plane *primary, 66024edb884SFrançois Tigeot struct drm_plane *cursor, 661aee94f86SFrançois Tigeot const struct drm_crtc_funcs *funcs, 662aee94f86SFrançois Tigeot const char *name, ...) 6635718399fSFrançois Tigeot { 664ba55f2f5SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 6655718399fSFrançois Tigeot int ret; 6665718399fSFrançois Tigeot 667477eb7f9SFrançois Tigeot WARN_ON(primary && primary->type != DRM_PLANE_TYPE_PRIMARY); 668477eb7f9SFrançois Tigeot WARN_ON(cursor && cursor->type != DRM_PLANE_TYPE_CURSOR); 669477eb7f9SFrançois Tigeot 6705718399fSFrançois Tigeot crtc->dev = dev; 6715718399fSFrançois Tigeot crtc->funcs = funcs; 6725718399fSFrançois Tigeot 673ba55f2f5SFrançois Tigeot drm_modeset_lock_init(&crtc->mutex); 6745718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &crtc->base, DRM_MODE_OBJECT_CRTC); 6755718399fSFrançois Tigeot if (ret) 67619c468b4SFrançois Tigeot return ret; 6775718399fSFrançois Tigeot 678aee94f86SFrançois Tigeot if (name) { 679aee94f86SFrançois Tigeot __va_list ap; 680aee94f86SFrançois Tigeot 681aee94f86SFrançois Tigeot __va_start(ap, name); 682aee94f86SFrançois Tigeot crtc->name = kvasprintf(GFP_KERNEL, name, ap); 683aee94f86SFrançois Tigeot __va_end(ap); 684aee94f86SFrançois Tigeot } else { 685aee94f86SFrançois Tigeot crtc->name = kasprintf(GFP_KERNEL, "crtc-%d", 686aee94f86SFrançois Tigeot drm_num_crtcs(dev)); 687aee94f86SFrançois Tigeot } 688aee94f86SFrançois Tigeot if (!crtc->name) { 6898621f407SFrançois Tigeot drm_mode_object_unregister(dev, &crtc->base); 690aee94f86SFrançois Tigeot return -ENOMEM; 691aee94f86SFrançois Tigeot } 692aee94f86SFrançois Tigeot 693b5162e19SFrançois Tigeot crtc->base.properties = &crtc->properties; 694b5162e19SFrançois Tigeot 695ba55f2f5SFrançois Tigeot list_add_tail(&crtc->head, &config->crtc_list); 696ba55f2f5SFrançois Tigeot config->num_crtc++; 697ba55f2f5SFrançois Tigeot 698ba55f2f5SFrançois Tigeot crtc->primary = primary; 69924edb884SFrançois Tigeot crtc->cursor = cursor; 700ba55f2f5SFrançois Tigeot if (primary) 701ba55f2f5SFrançois Tigeot primary->possible_crtcs = 1 << drm_crtc_index(crtc); 70224edb884SFrançois Tigeot if (cursor) 70324edb884SFrançois Tigeot cursor->possible_crtcs = 1 << drm_crtc_index(crtc); 704b5162e19SFrançois Tigeot 70519c468b4SFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 70619c468b4SFrançois Tigeot drm_object_attach_property(&crtc->base, config->prop_active, 0); 70719c468b4SFrançois Tigeot drm_object_attach_property(&crtc->base, config->prop_mode_id, 0); 70819c468b4SFrançois Tigeot } 7095718399fSFrançois Tigeot 71019c468b4SFrançois Tigeot return 0; 7115718399fSFrançois Tigeot } 712ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_init_with_planes); 7135718399fSFrançois Tigeot 7145718399fSFrançois Tigeot /** 71506fede5aSFrançois Tigeot * drm_crtc_cleanup - Clean up the core crtc usage 7165718399fSFrançois Tigeot * @crtc: CRTC to cleanup 7175718399fSFrançois Tigeot * 71806fede5aSFrançois Tigeot * This function cleans up @crtc and removes it from the DRM mode setting 71906fede5aSFrançois Tigeot * core. Note that the function does *not* free the crtc structure itself, 72006fede5aSFrançois Tigeot * this is the responsibility of the caller. 7215718399fSFrançois Tigeot */ 7225718399fSFrançois Tigeot void drm_crtc_cleanup(struct drm_crtc *crtc) 7235718399fSFrançois Tigeot { 7245718399fSFrançois Tigeot struct drm_device *dev = crtc->dev; 7255718399fSFrançois Tigeot 7264dbb207bSFrançois Tigeot kfree(crtc->gamma_store); 7275718399fSFrançois Tigeot crtc->gamma_store = NULL; 7285718399fSFrançois Tigeot 729ba55f2f5SFrançois Tigeot drm_modeset_lock_fini(&crtc->mutex); 730ba55f2f5SFrançois Tigeot 7318621f407SFrançois Tigeot drm_mode_object_unregister(dev, &crtc->base); 7325718399fSFrançois Tigeot list_del(&crtc->head); 7335718399fSFrançois Tigeot dev->mode_config.num_crtc--; 7342c9916cdSFrançois Tigeot 7352c9916cdSFrançois Tigeot WARN_ON(crtc->state && !crtc->funcs->atomic_destroy_state); 7362c9916cdSFrançois Tigeot if (crtc->state && crtc->funcs->atomic_destroy_state) 7372c9916cdSFrançois Tigeot crtc->funcs->atomic_destroy_state(crtc, crtc->state); 7382c9916cdSFrançois Tigeot 739aee94f86SFrançois Tigeot kfree(crtc->name); 740aee94f86SFrançois Tigeot 7412c9916cdSFrançois Tigeot memset(crtc, 0, sizeof(*crtc)); 7425718399fSFrançois Tigeot } 743b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_cleanup); 7445718399fSFrançois Tigeot 7455718399fSFrançois Tigeot /** 7469edbd4a0SFrançois Tigeot * drm_crtc_index - find the index of a registered CRTC 7479edbd4a0SFrançois Tigeot * @crtc: CRTC to find index for 7489edbd4a0SFrançois Tigeot * 7499edbd4a0SFrançois Tigeot * Given a registered CRTC, return the index of that CRTC within a DRM 7509edbd4a0SFrançois Tigeot * device's list of CRTCs. 7519edbd4a0SFrançois Tigeot */ 7529edbd4a0SFrançois Tigeot unsigned int drm_crtc_index(struct drm_crtc *crtc) 7539edbd4a0SFrançois Tigeot { 7549edbd4a0SFrançois Tigeot unsigned int index = 0; 7559edbd4a0SFrançois Tigeot struct drm_crtc *tmp; 7569edbd4a0SFrançois Tigeot 757a05eeebfSFrançois Tigeot drm_for_each_crtc(tmp, crtc->dev) { 7589edbd4a0SFrançois Tigeot if (tmp == crtc) 7599edbd4a0SFrançois Tigeot return index; 7609edbd4a0SFrançois Tigeot 7619edbd4a0SFrançois Tigeot index++; 7629edbd4a0SFrançois Tigeot } 7639edbd4a0SFrançois Tigeot 7649edbd4a0SFrançois Tigeot BUG(); 7659edbd4a0SFrançois Tigeot } 7669edbd4a0SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_index); 7679edbd4a0SFrançois Tigeot 7689edbd4a0SFrançois Tigeot /* 7695718399fSFrançois Tigeot * drm_mode_remove - remove and free a mode 7705718399fSFrançois Tigeot * @connector: connector list to modify 7715718399fSFrançois Tigeot * @mode: mode to remove 7725718399fSFrançois Tigeot * 7735718399fSFrançois Tigeot * Remove @mode from @connector's mode list, then free it. 7745718399fSFrançois Tigeot */ 7759edbd4a0SFrançois Tigeot static void drm_mode_remove(struct drm_connector *connector, 7765718399fSFrançois Tigeot struct drm_display_mode *mode) 7775718399fSFrançois Tigeot { 7785718399fSFrançois Tigeot list_del(&mode->head); 7795718399fSFrançois Tigeot drm_mode_destroy(connector->dev, mode); 7805718399fSFrançois Tigeot } 7815718399fSFrançois Tigeot 7825718399fSFrançois Tigeot /** 7832c9916cdSFrançois Tigeot * drm_display_info_set_bus_formats - set the supported bus formats 7842c9916cdSFrançois Tigeot * @info: display info to store bus formats in 785917ec290SFrançois Tigeot * @formats: array containing the supported bus formats 786917ec290SFrançois Tigeot * @num_formats: the number of entries in the fmts array 7872c9916cdSFrançois Tigeot * 7882c9916cdSFrançois Tigeot * Store the supported bus formats in display info structure. 7892c9916cdSFrançois Tigeot * See MEDIA_BUS_FMT_* definitions in include/uapi/linux/media-bus-format.h for 7902c9916cdSFrançois Tigeot * a full list of available formats. 7912c9916cdSFrançois Tigeot */ 7922c9916cdSFrançois Tigeot int drm_display_info_set_bus_formats(struct drm_display_info *info, 7932c9916cdSFrançois Tigeot const u32 *formats, 7942c9916cdSFrançois Tigeot unsigned int num_formats) 7952c9916cdSFrançois Tigeot { 7962c9916cdSFrançois Tigeot u32 *fmts = NULL; 7972c9916cdSFrançois Tigeot 7982c9916cdSFrançois Tigeot if (!formats && num_formats) 7992c9916cdSFrançois Tigeot return -EINVAL; 8002c9916cdSFrançois Tigeot 8012c9916cdSFrançois Tigeot if (formats && num_formats) { 8022c9916cdSFrançois Tigeot fmts = kmemdup(formats, sizeof(*formats) * num_formats, 8032c9916cdSFrançois Tigeot GFP_KERNEL); 8042c9916cdSFrançois Tigeot if (!fmts) 8052c9916cdSFrançois Tigeot return -ENOMEM; 8062c9916cdSFrançois Tigeot } 8072c9916cdSFrançois Tigeot 8082c9916cdSFrançois Tigeot kfree(info->bus_formats); 8092c9916cdSFrançois Tigeot info->bus_formats = fmts; 8102c9916cdSFrançois Tigeot info->num_bus_formats = num_formats; 8112c9916cdSFrançois Tigeot 8122c9916cdSFrançois Tigeot return 0; 8132c9916cdSFrançois Tigeot } 8142c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_display_info_set_bus_formats); 8152c9916cdSFrançois Tigeot 8162c9916cdSFrançois Tigeot /** 8171b13d190SFrançois Tigeot * drm_connector_get_cmdline_mode - reads the user's cmdline mode 8181b13d190SFrançois Tigeot * @connector: connector to quwery 8191b13d190SFrançois Tigeot * 8201b13d190SFrançois Tigeot * The kernel supports per-connector configration of its consoles through 8211b13d190SFrançois Tigeot * use of the video= parameter. This function parses that option and 8221b13d190SFrançois Tigeot * extracts the user's specified mode (or enable/disable status) for a 8231b13d190SFrançois Tigeot * particular connector. This is typically only used during the early fbdev 8241b13d190SFrançois Tigeot * setup. 8251b13d190SFrançois Tigeot */ 8261b13d190SFrançois Tigeot static void drm_connector_get_cmdline_mode(struct drm_connector *connector) 8271b13d190SFrançois Tigeot { 8281b13d190SFrançois Tigeot struct drm_cmdline_mode *mode = &connector->cmdline_mode; 8291b13d190SFrançois Tigeot char *option = NULL; 8301b13d190SFrançois Tigeot 8311b13d190SFrançois Tigeot if (fb_get_options(connector->name, &option)) 8321b13d190SFrançois Tigeot return; 8331b13d190SFrançois Tigeot 8341b13d190SFrançois Tigeot if (!drm_mode_parse_command_line_for_connector(option, 8351b13d190SFrançois Tigeot connector, 8361b13d190SFrançois Tigeot mode)) 8371b13d190SFrançois Tigeot return; 8381b13d190SFrançois Tigeot 8391b13d190SFrançois Tigeot if (mode->force) { 8401b13d190SFrançois Tigeot const char *s; 8411b13d190SFrançois Tigeot 8421b13d190SFrançois Tigeot switch (mode->force) { 8431b13d190SFrançois Tigeot case DRM_FORCE_OFF: 8441b13d190SFrançois Tigeot s = "OFF"; 8451b13d190SFrançois Tigeot break; 8461b13d190SFrançois Tigeot case DRM_FORCE_ON_DIGITAL: 8471b13d190SFrançois Tigeot s = "ON - dig"; 8481b13d190SFrançois Tigeot break; 8491b13d190SFrançois Tigeot default: 8501b13d190SFrançois Tigeot case DRM_FORCE_ON: 8511b13d190SFrançois Tigeot s = "ON"; 8521b13d190SFrançois Tigeot break; 8531b13d190SFrançois Tigeot } 8541b13d190SFrançois Tigeot 8551b13d190SFrançois Tigeot DRM_INFO("forcing %s connector %s\n", connector->name, s); 8561b13d190SFrançois Tigeot connector->force = mode->force; 8571b13d190SFrançois Tigeot } 8581b13d190SFrançois Tigeot 8591b13d190SFrançois Tigeot DRM_DEBUG_KMS("cmdline mode for connector %s %dx%d@%dHz%s%s%s\n", 8601b13d190SFrançois Tigeot connector->name, 8611b13d190SFrançois Tigeot mode->xres, mode->yres, 8621b13d190SFrançois Tigeot mode->refresh_specified ? mode->refresh : 60, 8631b13d190SFrançois Tigeot mode->rb ? " reduced blanking" : "", 8641b13d190SFrançois Tigeot mode->margins ? " with margins" : "", 8651b13d190SFrançois Tigeot mode->interlace ? " interlaced" : ""); 8661b13d190SFrançois Tigeot } 8671b13d190SFrançois Tigeot 8688621f407SFrançois Tigeot static void drm_connector_free(struct kref *kref) 8698621f407SFrançois Tigeot { 8708621f407SFrançois Tigeot struct drm_connector *connector = 8718621f407SFrançois Tigeot container_of(kref, struct drm_connector, base.refcount); 8728621f407SFrançois Tigeot struct drm_device *dev = connector->dev; 8738621f407SFrançois Tigeot 8748621f407SFrançois Tigeot drm_mode_object_unregister(dev, &connector->base); 8758621f407SFrançois Tigeot connector->funcs->destroy(connector); 8768621f407SFrançois Tigeot } 8778621f407SFrançois Tigeot 8781b13d190SFrançois Tigeot /** 8795718399fSFrançois Tigeot * drm_connector_init - Init a preallocated connector 8805718399fSFrançois Tigeot * @dev: DRM device 8815718399fSFrançois Tigeot * @connector: the connector to init 8825718399fSFrançois Tigeot * @funcs: callbacks for this connector 883a2fdbec6SFrançois Tigeot * @connector_type: user visible type of the connector 8845718399fSFrançois Tigeot * 8855718399fSFrançois Tigeot * Initialises a preallocated connector. Connectors should be 8865718399fSFrançois Tigeot * subclassed as part of driver connector objects. 8875718399fSFrançois Tigeot * 888ba55f2f5SFrançois Tigeot * Returns: 8895718399fSFrançois Tigeot * Zero on success, error code on failure. 8905718399fSFrançois Tigeot */ 8915718399fSFrançois Tigeot int drm_connector_init(struct drm_device *dev, 8925718399fSFrançois Tigeot struct drm_connector *connector, 8935718399fSFrançois Tigeot const struct drm_connector_funcs *funcs, 8945718399fSFrançois Tigeot int connector_type) 8955718399fSFrançois Tigeot { 8962c9916cdSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 8975718399fSFrançois Tigeot int ret; 8985718399fSFrançois Tigeot 8994dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 9005718399fSFrançois Tigeot 9018621f407SFrançois Tigeot ret = drm_mode_object_get_reg(dev, &connector->base, 9028621f407SFrançois Tigeot DRM_MODE_OBJECT_CONNECTOR, 9038621f407SFrançois Tigeot true, drm_connector_free); 9045718399fSFrançois Tigeot if (ret) 905ba55f2f5SFrançois Tigeot goto out_unlock; 9065718399fSFrançois Tigeot 907b5162e19SFrançois Tigeot connector->base.properties = &connector->properties; 9085718399fSFrançois Tigeot connector->dev = dev; 9095718399fSFrançois Tigeot connector->funcs = funcs; 9108621f407SFrançois Tigeot 9115718399fSFrançois Tigeot connector->connector_type = connector_type; 9125718399fSFrançois Tigeot connector->connector_type_id = 913c7dca6a7SFrançois Tigeot ++drm_connector_enum_list[connector_type].count; /* TODO */ 9149edbd4a0SFrançois Tigeot if (connector->connector_type_id < 0) { 9159edbd4a0SFrançois Tigeot ret = connector->connector_type_id; 916c7dca6a7SFrançois Tigeot goto out_put; 9179edbd4a0SFrançois Tigeot } 918b4efbf42Szrj connector->name = 919c0e85e96SFrançois Tigeot kasprintf(GFP_KERNEL, "%s-%d", 920b4efbf42Szrj drm_connector_enum_list[connector_type].name, 921b4efbf42Szrj connector->connector_type_id); 922b4efbf42Szrj if (!connector->name) { 923b4efbf42Szrj ret = -ENOMEM; 924c7dca6a7SFrançois Tigeot goto out_put; 925b4efbf42Szrj } 926ba55f2f5SFrançois Tigeot 9275718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->probed_modes); 9285718399fSFrançois Tigeot INIT_LIST_HEAD(&connector->modes); 9295718399fSFrançois Tigeot connector->edid_blob_ptr = NULL; 930b5162e19SFrançois Tigeot connector->status = connector_status_unknown; 9315718399fSFrançois Tigeot 9321b13d190SFrançois Tigeot drm_connector_get_cmdline_mode(connector); 9331b13d190SFrançois Tigeot 9342c9916cdSFrançois Tigeot /* We should add connectors at the end to avoid upsetting the connector 9352c9916cdSFrançois Tigeot * index too much. */ 9362c9916cdSFrançois Tigeot list_add_tail(&connector->head, &config->connector_list); 9372c9916cdSFrançois Tigeot config->num_connector++; 9385718399fSFrançois Tigeot 939b5162e19SFrançois Tigeot if (connector_type != DRM_MODE_CONNECTOR_VIRTUAL) 940b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 9412c9916cdSFrançois Tigeot config->edid_property, 942b5162e19SFrançois Tigeot 0); 9435718399fSFrançois Tigeot 944b5162e19SFrançois Tigeot drm_object_attach_property(&connector->base, 9452c9916cdSFrançois Tigeot config->dpms_property, 0); 9462c9916cdSFrançois Tigeot 9472c9916cdSFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 9482c9916cdSFrançois Tigeot drm_object_attach_property(&connector->base, config->prop_crtc_id, 0); 9492c9916cdSFrançois Tigeot } 9505718399fSFrançois Tigeot 95124edb884SFrançois Tigeot connector->debugfs_entry = NULL; 952c7dca6a7SFrançois Tigeot 953ba55f2f5SFrançois Tigeot out_put: 954ba55f2f5SFrançois Tigeot if (ret) 9558621f407SFrançois Tigeot drm_mode_object_unregister(dev, &connector->base); 956ba55f2f5SFrançois Tigeot 957ba55f2f5SFrançois Tigeot out_unlock: 9584dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 9595718399fSFrançois Tigeot 9605718399fSFrançois Tigeot return ret; 9615718399fSFrançois Tigeot } 962b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_init); 9635718399fSFrançois Tigeot 9645718399fSFrançois Tigeot /** 9655718399fSFrançois Tigeot * drm_connector_cleanup - cleans up an initialised connector 9665718399fSFrançois Tigeot * @connector: connector to cleanup 9675718399fSFrançois Tigeot * 9685718399fSFrançois Tigeot * Cleans up the connector but doesn't free the object. 9695718399fSFrançois Tigeot */ 9705718399fSFrançois Tigeot void drm_connector_cleanup(struct drm_connector *connector) 9715718399fSFrançois Tigeot { 9725718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 9735718399fSFrançois Tigeot struct drm_display_mode *mode, *t; 9745718399fSFrançois Tigeot 9755718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->probed_modes, head) 9765718399fSFrançois Tigeot drm_mode_remove(connector, mode); 9775718399fSFrançois Tigeot 9785718399fSFrançois Tigeot list_for_each_entry_safe(mode, t, &connector->modes, head) 9795718399fSFrançois Tigeot drm_mode_remove(connector, mode); 9805718399fSFrançois Tigeot 981917ec290SFrançois Tigeot ida_remove(&drm_connector_enum_list[connector->connector_type].ida, 982917ec290SFrançois Tigeot connector->connector_type_id); 983917ec290SFrançois Tigeot 984917ec290SFrançois Tigeot ida_remove(&dev->mode_config.connector_ida, 985917ec290SFrançois Tigeot connector->connector_id); 986917ec290SFrançois Tigeot 9872c9916cdSFrançois Tigeot kfree(connector->display_info.bus_formats); 9888621f407SFrançois Tigeot drm_mode_object_unregister(dev, &connector->base); 989b4efbf42Szrj kfree(connector->name); 990b4efbf42Szrj connector->name = NULL; 9915718399fSFrançois Tigeot list_del(&connector->head); 9925718399fSFrançois Tigeot dev->mode_config.num_connector--; 9932c9916cdSFrançois Tigeot 9942c9916cdSFrançois Tigeot WARN_ON(connector->state && !connector->funcs->atomic_destroy_state); 9952c9916cdSFrançois Tigeot if (connector->state && connector->funcs->atomic_destroy_state) 9962c9916cdSFrançois Tigeot connector->funcs->atomic_destroy_state(connector, 9972c9916cdSFrançois Tigeot connector->state); 9982c9916cdSFrançois Tigeot 9992c9916cdSFrançois Tigeot memset(connector, 0, sizeof(*connector)); 10005718399fSFrançois Tigeot } 1001b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_connector_cleanup); 1002b5162e19SFrançois Tigeot 1003ba55f2f5SFrançois Tigeot /** 1004c7dca6a7SFrançois Tigeot * drm_connector_index - find the index of a registered connector 1005c7dca6a7SFrançois Tigeot * @connector: connector to find index for 1006c7dca6a7SFrançois Tigeot * 1007c7dca6a7SFrançois Tigeot * Given a registered connector, return the index of that connector within a DRM 1008c7dca6a7SFrançois Tigeot * device's list of connectors. 1009c7dca6a7SFrançois Tigeot */ 1010c7dca6a7SFrançois Tigeot unsigned int drm_connector_index(struct drm_connector *connector) 1011c7dca6a7SFrançois Tigeot { 1012c7dca6a7SFrançois Tigeot unsigned int index = 0; 1013c7dca6a7SFrançois Tigeot struct drm_connector *tmp; 1014c7dca6a7SFrançois Tigeot struct drm_mode_config *config = &connector->dev->mode_config; 1015c7dca6a7SFrançois Tigeot 1016c7dca6a7SFrançois Tigeot WARN_ON(!drm_modeset_is_locked(&config->connection_mutex)); 1017c7dca6a7SFrançois Tigeot 1018c7dca6a7SFrançois Tigeot drm_for_each_connector(tmp, connector->dev) { 1019c7dca6a7SFrançois Tigeot if (tmp == connector) 1020c7dca6a7SFrançois Tigeot return index; 1021c7dca6a7SFrançois Tigeot 1022c7dca6a7SFrançois Tigeot index++; 1023c7dca6a7SFrançois Tigeot } 1024c7dca6a7SFrançois Tigeot 1025c7dca6a7SFrançois Tigeot BUG(); 1026c7dca6a7SFrançois Tigeot } 1027c7dca6a7SFrançois Tigeot EXPORT_SYMBOL(drm_connector_index); 1028c7dca6a7SFrançois Tigeot 1029c7dca6a7SFrançois Tigeot /** 1030c6f73aabSFrançois Tigeot * drm_connector_register - register a connector 1031c6f73aabSFrançois Tigeot * @connector: the connector to register 1032c6f73aabSFrançois Tigeot * 1033c6f73aabSFrançois Tigeot * Register userspace interfaces for a connector 1034c6f73aabSFrançois Tigeot * 1035c6f73aabSFrançois Tigeot * Returns: 1036c6f73aabSFrançois Tigeot * Zero on success, error code on failure. 1037c6f73aabSFrançois Tigeot */ 1038c6f73aabSFrançois Tigeot int drm_connector_register(struct drm_connector *connector) 1039c6f73aabSFrançois Tigeot { 104024edb884SFrançois Tigeot int ret; 104124edb884SFrançois Tigeot 104224edb884SFrançois Tigeot ret = drm_sysfs_connector_add(connector); 104324edb884SFrançois Tigeot if (ret) 104424edb884SFrançois Tigeot return ret; 104524edb884SFrançois Tigeot 104624edb884SFrançois Tigeot ret = drm_debugfs_connector_add(connector); 104724edb884SFrançois Tigeot if (ret) { 104824edb884SFrançois Tigeot drm_sysfs_connector_remove(connector); 104924edb884SFrançois Tigeot return ret; 105024edb884SFrançois Tigeot } 105124edb884SFrançois Tigeot 10528621f407SFrançois Tigeot drm_mode_object_register(connector->dev, &connector->base); 10538621f407SFrançois Tigeot 105424edb884SFrançois Tigeot return 0; 1055c6f73aabSFrançois Tigeot } 1056c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_register); 1057c6f73aabSFrançois Tigeot 1058c6f73aabSFrançois Tigeot /** 1059c6f73aabSFrançois Tigeot * drm_connector_unregister - unregister a connector 1060c6f73aabSFrançois Tigeot * @connector: the connector to unregister 1061c6f73aabSFrançois Tigeot * 1062c6f73aabSFrançois Tigeot * Unregister userspace interfaces for a connector 1063c6f73aabSFrançois Tigeot */ 1064c6f73aabSFrançois Tigeot void drm_connector_unregister(struct drm_connector *connector) 1065c6f73aabSFrançois Tigeot { 1066c6f73aabSFrançois Tigeot drm_sysfs_connector_remove(connector); 106724edb884SFrançois Tigeot #if 0 106824edb884SFrançois Tigeot drm_debugfs_connector_remove(connector); 106924edb884SFrançois Tigeot #endif 1070c6f73aabSFrançois Tigeot } 1071c6f73aabSFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister); 1072c6f73aabSFrançois Tigeot 1073c6f73aabSFrançois Tigeot /** 10748621f407SFrançois Tigeot * drm_connector_register_all - register all connectors 1075ba55f2f5SFrançois Tigeot * @dev: drm device 1076ba55f2f5SFrançois Tigeot * 10778621f407SFrançois Tigeot * This function registers all connectors in sysfs and other places so that 10788621f407SFrançois Tigeot * userspace can start to access them. Drivers can call it after calling 10798621f407SFrançois Tigeot * drm_dev_register() to complete the device registration, if they don't call 10808621f407SFrançois Tigeot * drm_connector_register() on each connector individually. 10818621f407SFrançois Tigeot * 10828621f407SFrançois Tigeot * When a device is unplugged and should be removed from userspace access, 10838621f407SFrançois Tigeot * call drm_connector_unregister_all(), which is the inverse of this 10848621f407SFrançois Tigeot * function. 10858621f407SFrançois Tigeot * 10868621f407SFrançois Tigeot * Returns: 10878621f407SFrançois Tigeot * Zero on success, error code on failure. 1088ba55f2f5SFrançois Tigeot */ 10898621f407SFrançois Tigeot int drm_connector_register_all(struct drm_device *dev) 10908621f407SFrançois Tigeot { 10918621f407SFrançois Tigeot struct drm_connector *connector; 10928621f407SFrançois Tigeot int ret; 10938621f407SFrançois Tigeot 10948621f407SFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 10958621f407SFrançois Tigeot 10968621f407SFrançois Tigeot drm_for_each_connector(connector, dev) { 10978621f407SFrançois Tigeot ret = drm_connector_register(connector); 10988621f407SFrançois Tigeot if (ret) 10998621f407SFrançois Tigeot goto err; 11008621f407SFrançois Tigeot } 11018621f407SFrançois Tigeot 11028621f407SFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 11038621f407SFrançois Tigeot 11048621f407SFrançois Tigeot return 0; 11058621f407SFrançois Tigeot 11068621f407SFrançois Tigeot err: 11078621f407SFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 11088621f407SFrançois Tigeot drm_connector_unregister_all(dev); 11098621f407SFrançois Tigeot return ret; 11108621f407SFrançois Tigeot } 11118621f407SFrançois Tigeot EXPORT_SYMBOL(drm_connector_register_all); 11128621f407SFrançois Tigeot 11138621f407SFrançois Tigeot /** 11148621f407SFrançois Tigeot * drm_connector_unregister_all - unregister connector userspace interfaces 11158621f407SFrançois Tigeot * @dev: drm device 11168621f407SFrançois Tigeot * 11178621f407SFrançois Tigeot * This functions unregisters all connectors from sysfs and other places so 11188621f407SFrançois Tigeot * that userspace can no longer access them. Drivers should call this as the 11198621f407SFrançois Tigeot * first step tearing down the device instace, or when the underlying 11208621f407SFrançois Tigeot * physical device disappeared (e.g. USB unplug), right before calling 11218621f407SFrançois Tigeot * drm_dev_unregister(). 11228621f407SFrançois Tigeot */ 11238621f407SFrançois Tigeot void drm_connector_unregister_all(struct drm_device *dev) 1124b5162e19SFrançois Tigeot { 1125b5162e19SFrançois Tigeot struct drm_connector *connector; 1126b5162e19SFrançois Tigeot 1127a05eeebfSFrançois Tigeot /* FIXME: taking the mode config mutex ends up in a clash with sysfs */ 1128b5162e19SFrançois Tigeot list_for_each_entry(connector, &dev->mode_config.connector_list, head) 1129c6f73aabSFrançois Tigeot drm_connector_unregister(connector); 1130b5162e19SFrançois Tigeot } 11318621f407SFrançois Tigeot EXPORT_SYMBOL(drm_connector_unregister_all); 11325718399fSFrançois Tigeot 1133ba55f2f5SFrançois Tigeot /** 1134ba55f2f5SFrançois Tigeot * drm_encoder_init - Init a preallocated encoder 1135ba55f2f5SFrançois Tigeot * @dev: drm device 1136ba55f2f5SFrançois Tigeot * @encoder: the encoder to init 1137ba55f2f5SFrançois Tigeot * @funcs: callbacks for this encoder 1138ba55f2f5SFrançois Tigeot * @encoder_type: user visible type of the encoder 1139aee94f86SFrançois Tigeot * @name: printf style format string for the encoder name, or NULL for default name 1140ba55f2f5SFrançois Tigeot * 1141ba55f2f5SFrançois Tigeot * Initialises a preallocated encoder. Encoder should be 1142ba55f2f5SFrançois Tigeot * subclassed as part of driver encoder objects. 1143ba55f2f5SFrançois Tigeot * 1144ba55f2f5SFrançois Tigeot * Returns: 1145ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 1146ba55f2f5SFrançois Tigeot */ 11475718399fSFrançois Tigeot int drm_encoder_init(struct drm_device *dev, 11485718399fSFrançois Tigeot struct drm_encoder *encoder, 11495718399fSFrançois Tigeot const struct drm_encoder_funcs *funcs, 1150aee94f86SFrançois Tigeot int encoder_type, const char *name, ...) 11515718399fSFrançois Tigeot { 11525718399fSFrançois Tigeot int ret; 11535718399fSFrançois Tigeot 11544dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 11555718399fSFrançois Tigeot 11565718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &encoder->base, DRM_MODE_OBJECT_ENCODER); 11575718399fSFrançois Tigeot if (ret) 1158ba55f2f5SFrançois Tigeot goto out_unlock; 11595718399fSFrançois Tigeot 11605718399fSFrançois Tigeot encoder->dev = dev; 11615718399fSFrançois Tigeot encoder->encoder_type = encoder_type; 11625718399fSFrançois Tigeot encoder->funcs = funcs; 1163aee94f86SFrançois Tigeot if (name) { 1164aee94f86SFrançois Tigeot __va_list ap; 1165aee94f86SFrançois Tigeot 1166aee94f86SFrançois Tigeot __va_start(ap, name); 1167aee94f86SFrançois Tigeot encoder->name = kvasprintf(GFP_KERNEL, name, ap); 1168aee94f86SFrançois Tigeot __va_end(ap); 1169aee94f86SFrançois Tigeot } else { 1170aee94f86SFrançois Tigeot encoder->name = kasprintf(GFP_KERNEL, "%s-%d", 1171b4efbf42Szrj drm_encoder_enum_list[encoder_type].name, 1172b4efbf42Szrj encoder->base.id); 1173aee94f86SFrançois Tigeot } 1174b4efbf42Szrj if (!encoder->name) { 1175b4efbf42Szrj ret = -ENOMEM; 1176b4efbf42Szrj goto out_put; 1177b4efbf42Szrj } 11785718399fSFrançois Tigeot 11795718399fSFrançois Tigeot list_add_tail(&encoder->head, &dev->mode_config.encoder_list); 11805718399fSFrançois Tigeot dev->mode_config.num_encoder++; 11815718399fSFrançois Tigeot 1182b4efbf42Szrj out_put: 1183b4efbf42Szrj if (ret) 11848621f407SFrançois Tigeot drm_mode_object_unregister(dev, &encoder->base); 1185b4efbf42Szrj 1186ba55f2f5SFrançois Tigeot out_unlock: 11874dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 11885718399fSFrançois Tigeot 11895718399fSFrançois Tigeot return ret; 11905718399fSFrançois Tigeot } 1191b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_init); 11925718399fSFrançois Tigeot 1193ba55f2f5SFrançois Tigeot /** 1194c0e85e96SFrançois Tigeot * drm_encoder_index - find the index of a registered encoder 1195c0e85e96SFrançois Tigeot * @encoder: encoder to find index for 1196c0e85e96SFrançois Tigeot * 1197c0e85e96SFrançois Tigeot * Given a registered encoder, return the index of that encoder within a DRM 1198c0e85e96SFrançois Tigeot * device's list of encoders. 1199c0e85e96SFrançois Tigeot */ 1200c0e85e96SFrançois Tigeot unsigned int drm_encoder_index(struct drm_encoder *encoder) 1201c0e85e96SFrançois Tigeot { 1202c0e85e96SFrançois Tigeot unsigned int index = 0; 1203c0e85e96SFrançois Tigeot struct drm_encoder *tmp; 1204c0e85e96SFrançois Tigeot 1205c0e85e96SFrançois Tigeot drm_for_each_encoder(tmp, encoder->dev) { 1206c0e85e96SFrançois Tigeot if (tmp == encoder) 1207c0e85e96SFrançois Tigeot return index; 1208c0e85e96SFrançois Tigeot 1209c0e85e96SFrançois Tigeot index++; 1210c0e85e96SFrançois Tigeot } 1211c0e85e96SFrançois Tigeot 1212c0e85e96SFrançois Tigeot BUG(); 1213c0e85e96SFrançois Tigeot } 1214c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_index); 1215c0e85e96SFrançois Tigeot 1216c0e85e96SFrançois Tigeot /** 1217ba55f2f5SFrançois Tigeot * drm_encoder_cleanup - cleans up an initialised encoder 1218ba55f2f5SFrançois Tigeot * @encoder: encoder to cleanup 1219ba55f2f5SFrançois Tigeot * 1220ba55f2f5SFrançois Tigeot * Cleans up the encoder but doesn't free the object. 1221ba55f2f5SFrançois Tigeot */ 12225718399fSFrançois Tigeot void drm_encoder_cleanup(struct drm_encoder *encoder) 12235718399fSFrançois Tigeot { 12245718399fSFrançois Tigeot struct drm_device *dev = encoder->dev; 12252c9916cdSFrançois Tigeot 12264dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 12278621f407SFrançois Tigeot drm_mode_object_unregister(dev, &encoder->base); 1228ba55f2f5SFrançois Tigeot kfree(encoder->name); 12295718399fSFrançois Tigeot list_del(&encoder->head); 12305718399fSFrançois Tigeot dev->mode_config.num_encoder--; 12314dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 12322c9916cdSFrançois Tigeot 12332c9916cdSFrançois Tigeot memset(encoder, 0, sizeof(*encoder)); 12345718399fSFrançois Tigeot } 1235b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_encoder_cleanup); 12365718399fSFrançois Tigeot 1237917ec290SFrançois Tigeot static unsigned int drm_num_planes(struct drm_device *dev) 1238917ec290SFrançois Tigeot { 1239917ec290SFrançois Tigeot unsigned int num = 0; 1240917ec290SFrançois Tigeot struct drm_plane *tmp; 1241917ec290SFrançois Tigeot 1242917ec290SFrançois Tigeot drm_for_each_plane(tmp, dev) { 1243917ec290SFrançois Tigeot num++; 1244917ec290SFrançois Tigeot } 1245917ec290SFrançois Tigeot 1246917ec290SFrançois Tigeot return num; 1247917ec290SFrançois Tigeot } 1248917ec290SFrançois Tigeot 124906fede5aSFrançois Tigeot /** 1250ba55f2f5SFrançois Tigeot * drm_universal_plane_init - Initialize a new universal plane object 125106fede5aSFrançois Tigeot * @dev: DRM device 125206fede5aSFrançois Tigeot * @plane: plane object to init 125306fede5aSFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 125406fede5aSFrançois Tigeot * @funcs: callbacks for the new plane 125506fede5aSFrançois Tigeot * @formats: array of supported formats (%DRM_FORMAT_*) 125606fede5aSFrançois Tigeot * @format_count: number of elements in @formats 1257ba55f2f5SFrançois Tigeot * @type: type of plane (overlay, primary, cursor) 1258aee94f86SFrançois Tigeot * @name: printf style format string for the plane name, or NULL for default name 125906fede5aSFrançois Tigeot * 1260ba55f2f5SFrançois Tigeot * Initializes a plane object of type @type. 126106fede5aSFrançois Tigeot * 1262ba55f2f5SFrançois Tigeot * Returns: 126306fede5aSFrançois Tigeot * Zero on success, error code on failure. 126406fede5aSFrançois Tigeot */ 1265ba55f2f5SFrançois Tigeot int drm_universal_plane_init(struct drm_device *dev, struct drm_plane *plane, 12665718399fSFrançois Tigeot unsigned long possible_crtcs, 12675718399fSFrançois Tigeot const struct drm_plane_funcs *funcs, 1268a05eeebfSFrançois Tigeot const uint32_t *formats, unsigned int format_count, 1269aee94f86SFrançois Tigeot enum drm_plane_type type, 1270aee94f86SFrançois Tigeot const char *name, ...) 12715718399fSFrançois Tigeot { 12722c9916cdSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 12735718399fSFrançois Tigeot int ret; 12745718399fSFrançois Tigeot 12755718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &plane->base, DRM_MODE_OBJECT_PLANE); 12765718399fSFrançois Tigeot if (ret) 12772c9916cdSFrançois Tigeot return ret; 12782c9916cdSFrançois Tigeot 12792c9916cdSFrançois Tigeot drm_modeset_lock_init(&plane->mutex); 12805718399fSFrançois Tigeot 1281b5162e19SFrançois Tigeot plane->base.properties = &plane->properties; 12825718399fSFrançois Tigeot plane->dev = dev; 12835718399fSFrançois Tigeot plane->funcs = funcs; 1284917ec290SFrançois Tigeot plane->format_types = kmalloc(format_count * sizeof(uint32_t), 12855a3b77d5SFrançois Tigeot M_DRM, M_WAITOK); 1286b5162e19SFrançois Tigeot if (!plane->format_types) { 1287b5162e19SFrançois Tigeot DRM_DEBUG_KMS("out of memory when allocating plane\n"); 12888621f407SFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 12892c9916cdSFrançois Tigeot return -ENOMEM; 1290b5162e19SFrançois Tigeot } 12915718399fSFrançois Tigeot 1292917ec290SFrançois Tigeot if (name) { 1293917ec290SFrançois Tigeot __va_list ap; 1294917ec290SFrançois Tigeot 1295917ec290SFrançois Tigeot __va_start(ap, name); 1296917ec290SFrançois Tigeot plane->name = kvasprintf(GFP_KERNEL, name, ap); 1297917ec290SFrançois Tigeot __va_end(ap); 1298917ec290SFrançois Tigeot } else { 1299917ec290SFrançois Tigeot plane->name = kasprintf(GFP_KERNEL, "plane-%d", 1300917ec290SFrançois Tigeot drm_num_planes(dev)); 1301917ec290SFrançois Tigeot } 1302917ec290SFrançois Tigeot if (!plane->name) { 1303917ec290SFrançois Tigeot kfree(plane->format_types); 13048621f407SFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 1305917ec290SFrançois Tigeot return -ENOMEM; 1306917ec290SFrançois Tigeot } 1307917ec290SFrançois Tigeot 13085718399fSFrançois Tigeot memcpy(plane->format_types, formats, format_count * sizeof(uint32_t)); 13095718399fSFrançois Tigeot plane->format_count = format_count; 13105718399fSFrançois Tigeot plane->possible_crtcs = possible_crtcs; 1311ba55f2f5SFrançois Tigeot plane->type = type; 13125718399fSFrançois Tigeot 13132c9916cdSFrançois Tigeot list_add_tail(&plane->head, &config->plane_list); 13142c9916cdSFrançois Tigeot config->num_total_plane++; 1315ba55f2f5SFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 13162c9916cdSFrançois Tigeot config->num_overlay_plane++; 1317ba55f2f5SFrançois Tigeot 1318ba55f2f5SFrançois Tigeot drm_object_attach_property(&plane->base, 13192c9916cdSFrançois Tigeot config->plane_type_property, 1320ba55f2f5SFrançois Tigeot plane->type); 13215718399fSFrançois Tigeot 13222c9916cdSFrançois Tigeot if (drm_core_check_feature(dev, DRIVER_ATOMIC)) { 13232c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_fb_id, 0); 13242c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_id, 0); 13252c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_x, 0); 13262c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_y, 0); 13272c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_w, 0); 13282c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_crtc_h, 0); 13292c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_x, 0); 13302c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_y, 0); 13312c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_w, 0); 13322c9916cdSFrançois Tigeot drm_object_attach_property(&plane->base, config->prop_src_h, 0); 13332c9916cdSFrançois Tigeot } 13345718399fSFrançois Tigeot 13352c9916cdSFrançois Tigeot return 0; 13365718399fSFrançois Tigeot } 1337ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_universal_plane_init); 1338ba55f2f5SFrançois Tigeot 1339ba55f2f5SFrançois Tigeot /** 1340ba55f2f5SFrançois Tigeot * drm_plane_init - Initialize a legacy plane 1341ba55f2f5SFrançois Tigeot * @dev: DRM device 1342ba55f2f5SFrançois Tigeot * @plane: plane object to init 1343ba55f2f5SFrançois Tigeot * @possible_crtcs: bitmask of possible CRTCs 1344ba55f2f5SFrançois Tigeot * @funcs: callbacks for the new plane 1345ba55f2f5SFrançois Tigeot * @formats: array of supported formats (%DRM_FORMAT_*) 1346ba55f2f5SFrançois Tigeot * @format_count: number of elements in @formats 1347ba55f2f5SFrançois Tigeot * @is_primary: plane type (primary vs overlay) 1348ba55f2f5SFrançois Tigeot * 1349ba55f2f5SFrançois Tigeot * Legacy API to initialize a DRM plane. 1350ba55f2f5SFrançois Tigeot * 1351ba55f2f5SFrançois Tigeot * New drivers should call drm_universal_plane_init() instead. 1352ba55f2f5SFrançois Tigeot * 1353ba55f2f5SFrançois Tigeot * Returns: 1354ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 1355ba55f2f5SFrançois Tigeot */ 1356ba55f2f5SFrançois Tigeot int drm_plane_init(struct drm_device *dev, struct drm_plane *plane, 1357ba55f2f5SFrançois Tigeot unsigned long possible_crtcs, 1358ba55f2f5SFrançois Tigeot const struct drm_plane_funcs *funcs, 1359a05eeebfSFrançois Tigeot const uint32_t *formats, unsigned int format_count, 1360ba55f2f5SFrançois Tigeot bool is_primary) 1361ba55f2f5SFrançois Tigeot { 1362ba55f2f5SFrançois Tigeot enum drm_plane_type type; 1363ba55f2f5SFrançois Tigeot 1364ba55f2f5SFrançois Tigeot type = is_primary ? DRM_PLANE_TYPE_PRIMARY : DRM_PLANE_TYPE_OVERLAY; 1365ba55f2f5SFrançois Tigeot return drm_universal_plane_init(dev, plane, possible_crtcs, funcs, 1366aee94f86SFrançois Tigeot formats, format_count, type, NULL); 1367ba55f2f5SFrançois Tigeot } 1368b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_init); 13695718399fSFrançois Tigeot 137006fede5aSFrançois Tigeot /** 137106fede5aSFrançois Tigeot * drm_plane_cleanup - Clean up the core plane usage 137206fede5aSFrançois Tigeot * @plane: plane to cleanup 137306fede5aSFrançois Tigeot * 137406fede5aSFrançois Tigeot * This function cleans up @plane and removes it from the DRM mode setting 137506fede5aSFrançois Tigeot * core. Note that the function does *not* free the plane structure itself, 137606fede5aSFrançois Tigeot * this is the responsibility of the caller. 137706fede5aSFrançois Tigeot */ 13785718399fSFrançois Tigeot void drm_plane_cleanup(struct drm_plane *plane) 13795718399fSFrançois Tigeot { 13805718399fSFrançois Tigeot struct drm_device *dev = plane->dev; 13815718399fSFrançois Tigeot 13824dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 13834dbb207bSFrançois Tigeot kfree(plane->format_types); 13848621f407SFrançois Tigeot drm_mode_object_unregister(dev, &plane->base); 1385ba55f2f5SFrançois Tigeot 1386ba55f2f5SFrançois Tigeot BUG_ON(list_empty(&plane->head)); 1387ba55f2f5SFrançois Tigeot 13885718399fSFrançois Tigeot list_del(&plane->head); 1389ba55f2f5SFrançois Tigeot dev->mode_config.num_total_plane--; 1390ba55f2f5SFrançois Tigeot if (plane->type == DRM_PLANE_TYPE_OVERLAY) 1391ba55f2f5SFrançois Tigeot dev->mode_config.num_overlay_plane--; 13924dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 13932c9916cdSFrançois Tigeot 13942c9916cdSFrançois Tigeot WARN_ON(plane->state && !plane->funcs->atomic_destroy_state); 13952c9916cdSFrançois Tigeot if (plane->state && plane->funcs->atomic_destroy_state) 13962c9916cdSFrançois Tigeot plane->funcs->atomic_destroy_state(plane, plane->state); 13972c9916cdSFrançois Tigeot 1398917ec290SFrançois Tigeot kfree(plane->name); 1399917ec290SFrançois Tigeot 14002c9916cdSFrançois Tigeot memset(plane, 0, sizeof(*plane)); 14015718399fSFrançois Tigeot } 1402b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_plane_cleanup); 14035718399fSFrançois Tigeot 14045718399fSFrançois Tigeot /** 14051b13d190SFrançois Tigeot * drm_plane_index - find the index of a registered plane 14061b13d190SFrançois Tigeot * @plane: plane to find index for 14071b13d190SFrançois Tigeot * 14081b13d190SFrançois Tigeot * Given a registered plane, return the index of that CRTC within a DRM 14091b13d190SFrançois Tigeot * device's list of planes. 14101b13d190SFrançois Tigeot */ 14111b13d190SFrançois Tigeot unsigned int drm_plane_index(struct drm_plane *plane) 14121b13d190SFrançois Tigeot { 14131b13d190SFrançois Tigeot unsigned int index = 0; 14141b13d190SFrançois Tigeot struct drm_plane *tmp; 14151b13d190SFrançois Tigeot 1416a05eeebfSFrançois Tigeot drm_for_each_plane(tmp, plane->dev) { 14171b13d190SFrançois Tigeot if (tmp == plane) 14181b13d190SFrançois Tigeot return index; 14191b13d190SFrançois Tigeot 14201b13d190SFrançois Tigeot index++; 14211b13d190SFrançois Tigeot } 14221b13d190SFrançois Tigeot 14231b13d190SFrançois Tigeot BUG(); 14241b13d190SFrançois Tigeot } 14251b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_plane_index); 14261b13d190SFrançois Tigeot 14271b13d190SFrançois Tigeot /** 142819c468b4SFrançois Tigeot * drm_plane_from_index - find the registered plane at an index 142919c468b4SFrançois Tigeot * @dev: DRM device 143019c468b4SFrançois Tigeot * @idx: index of registered plane to find for 143119c468b4SFrançois Tigeot * 143219c468b4SFrançois Tigeot * Given a plane index, return the registered plane from DRM device's 143319c468b4SFrançois Tigeot * list of planes with matching index. 143419c468b4SFrançois Tigeot */ 143519c468b4SFrançois Tigeot struct drm_plane * 143619c468b4SFrançois Tigeot drm_plane_from_index(struct drm_device *dev, int idx) 143719c468b4SFrançois Tigeot { 143819c468b4SFrançois Tigeot struct drm_plane *plane; 143919c468b4SFrançois Tigeot unsigned int i = 0; 144019c468b4SFrançois Tigeot 1441a05eeebfSFrançois Tigeot drm_for_each_plane(plane, dev) { 144219c468b4SFrançois Tigeot if (i == idx) 144319c468b4SFrançois Tigeot return plane; 144419c468b4SFrançois Tigeot i++; 144519c468b4SFrançois Tigeot } 144619c468b4SFrançois Tigeot return NULL; 144719c468b4SFrançois Tigeot } 144819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_plane_from_index); 144919c468b4SFrançois Tigeot 145019c468b4SFrançois Tigeot /** 145106fede5aSFrançois Tigeot * drm_plane_force_disable - Forcibly disable a plane 145206fede5aSFrançois Tigeot * @plane: plane to disable 145306fede5aSFrançois Tigeot * 145406fede5aSFrançois Tigeot * Forces the plane to be disabled. 145506fede5aSFrançois Tigeot * 145606fede5aSFrançois Tigeot * Used when the plane's current framebuffer is destroyed, 145706fede5aSFrançois Tigeot * and when restoring fbdev mode. 145806fede5aSFrançois Tigeot */ 145906fede5aSFrançois Tigeot void drm_plane_force_disable(struct drm_plane *plane) 146006fede5aSFrançois Tigeot { 146106fede5aSFrançois Tigeot int ret; 146206fede5aSFrançois Tigeot 14631b13d190SFrançois Tigeot if (!plane->fb) 146406fede5aSFrançois Tigeot return; 146506fede5aSFrançois Tigeot 14661b13d190SFrançois Tigeot plane->old_fb = plane->fb; 146706fede5aSFrançois Tigeot ret = plane->funcs->disable_plane(plane); 1468ba55f2f5SFrançois Tigeot if (ret) { 146906fede5aSFrançois Tigeot DRM_ERROR("failed to disable plane with busy fb\n"); 14701b13d190SFrançois Tigeot plane->old_fb = NULL; 1471ba55f2f5SFrançois Tigeot return; 1472ba55f2f5SFrançois Tigeot } 147306fede5aSFrançois Tigeot /* disconnect the plane from the fb and crtc: */ 14742c9916cdSFrançois Tigeot drm_framebuffer_unreference(plane->old_fb); 14751b13d190SFrançois Tigeot plane->old_fb = NULL; 147606fede5aSFrançois Tigeot plane->fb = NULL; 147706fede5aSFrançois Tigeot plane->crtc = NULL; 147806fede5aSFrançois Tigeot } 147906fede5aSFrançois Tigeot EXPORT_SYMBOL(drm_plane_force_disable); 148006fede5aSFrançois Tigeot 14812c9916cdSFrançois Tigeot static int drm_mode_create_standard_properties(struct drm_device *dev) 14825718399fSFrançois Tigeot { 14832c9916cdSFrançois Tigeot struct drm_property *prop; 14845718399fSFrançois Tigeot 14855718399fSFrançois Tigeot /* 14865718399fSFrançois Tigeot * Standard properties (apply to all connectors) 14875718399fSFrançois Tigeot */ 14882c9916cdSFrançois Tigeot prop = drm_property_create(dev, DRM_MODE_PROP_BLOB | 14895718399fSFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 14905718399fSFrançois Tigeot "EDID", 0); 14912c9916cdSFrançois Tigeot if (!prop) 14922c9916cdSFrançois Tigeot return -ENOMEM; 14932c9916cdSFrançois Tigeot dev->mode_config.edid_property = prop; 14945718399fSFrançois Tigeot 14952c9916cdSFrançois Tigeot prop = drm_property_create_enum(dev, 0, 14965718399fSFrançois Tigeot "DPMS", drm_dpms_enum_list, 1497b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dpms_enum_list)); 14982c9916cdSFrançois Tigeot if (!prop) 14992c9916cdSFrançois Tigeot return -ENOMEM; 15002c9916cdSFrançois Tigeot dev->mode_config.dpms_property = prop; 15015718399fSFrançois Tigeot 15022c9916cdSFrançois Tigeot prop = drm_property_create(dev, 150324edb884SFrançois Tigeot DRM_MODE_PROP_BLOB | 150424edb884SFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 150524edb884SFrançois Tigeot "PATH", 0); 15062c9916cdSFrançois Tigeot if (!prop) 15072c9916cdSFrançois Tigeot return -ENOMEM; 15082c9916cdSFrançois Tigeot dev->mode_config.path_property = prop; 150924edb884SFrançois Tigeot 15102c9916cdSFrançois Tigeot prop = drm_property_create(dev, 15112c9916cdSFrançois Tigeot DRM_MODE_PROP_BLOB | 15122c9916cdSFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 15132c9916cdSFrançois Tigeot "TILE", 0); 15142c9916cdSFrançois Tigeot if (!prop) 15152c9916cdSFrançois Tigeot return -ENOMEM; 15162c9916cdSFrançois Tigeot dev->mode_config.tile_property = prop; 15175718399fSFrançois Tigeot 15182c9916cdSFrançois Tigeot prop = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 1519ba55f2f5SFrançois Tigeot "type", drm_plane_type_enum_list, 1520ba55f2f5SFrançois Tigeot ARRAY_SIZE(drm_plane_type_enum_list)); 15212c9916cdSFrançois Tigeot if (!prop) 15222c9916cdSFrançois Tigeot return -ENOMEM; 15232c9916cdSFrançois Tigeot dev->mode_config.plane_type_property = prop; 15242c9916cdSFrançois Tigeot 15252c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15262c9916cdSFrançois Tigeot "SRC_X", 0, UINT_MAX); 15272c9916cdSFrançois Tigeot if (!prop) 15282c9916cdSFrançois Tigeot return -ENOMEM; 15292c9916cdSFrançois Tigeot dev->mode_config.prop_src_x = prop; 15302c9916cdSFrançois Tigeot 15312c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15322c9916cdSFrançois Tigeot "SRC_Y", 0, UINT_MAX); 15332c9916cdSFrançois Tigeot if (!prop) 15342c9916cdSFrançois Tigeot return -ENOMEM; 15352c9916cdSFrançois Tigeot dev->mode_config.prop_src_y = prop; 15362c9916cdSFrançois Tigeot 15372c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15382c9916cdSFrançois Tigeot "SRC_W", 0, UINT_MAX); 15392c9916cdSFrançois Tigeot if (!prop) 15402c9916cdSFrançois Tigeot return -ENOMEM; 15412c9916cdSFrançois Tigeot dev->mode_config.prop_src_w = prop; 15422c9916cdSFrançois Tigeot 15432c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15442c9916cdSFrançois Tigeot "SRC_H", 0, UINT_MAX); 15452c9916cdSFrançois Tigeot if (!prop) 15462c9916cdSFrançois Tigeot return -ENOMEM; 15472c9916cdSFrançois Tigeot dev->mode_config.prop_src_h = prop; 15482c9916cdSFrançois Tigeot 15492c9916cdSFrançois Tigeot prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, 15502c9916cdSFrançois Tigeot "CRTC_X", INT_MIN, INT_MAX); 15512c9916cdSFrançois Tigeot if (!prop) 15522c9916cdSFrançois Tigeot return -ENOMEM; 15532c9916cdSFrançois Tigeot dev->mode_config.prop_crtc_x = prop; 15542c9916cdSFrançois Tigeot 15552c9916cdSFrançois Tigeot prop = drm_property_create_signed_range(dev, DRM_MODE_PROP_ATOMIC, 15562c9916cdSFrançois Tigeot "CRTC_Y", INT_MIN, INT_MAX); 15572c9916cdSFrançois Tigeot if (!prop) 15582c9916cdSFrançois Tigeot return -ENOMEM; 15592c9916cdSFrançois Tigeot dev->mode_config.prop_crtc_y = prop; 15602c9916cdSFrançois Tigeot 15612c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15622c9916cdSFrançois Tigeot "CRTC_W", 0, INT_MAX); 15632c9916cdSFrançois Tigeot if (!prop) 15642c9916cdSFrançois Tigeot return -ENOMEM; 15652c9916cdSFrançois Tigeot dev->mode_config.prop_crtc_w = prop; 15662c9916cdSFrançois Tigeot 15672c9916cdSFrançois Tigeot prop = drm_property_create_range(dev, DRM_MODE_PROP_ATOMIC, 15682c9916cdSFrançois Tigeot "CRTC_H", 0, INT_MAX); 15692c9916cdSFrançois Tigeot if (!prop) 15702c9916cdSFrançois Tigeot return -ENOMEM; 15712c9916cdSFrançois Tigeot dev->mode_config.prop_crtc_h = prop; 15722c9916cdSFrançois Tigeot 15732c9916cdSFrançois Tigeot prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, 15742c9916cdSFrançois Tigeot "FB_ID", DRM_MODE_OBJECT_FB); 15752c9916cdSFrançois Tigeot if (!prop) 15762c9916cdSFrançois Tigeot return -ENOMEM; 15772c9916cdSFrançois Tigeot dev->mode_config.prop_fb_id = prop; 15782c9916cdSFrançois Tigeot 15792c9916cdSFrançois Tigeot prop = drm_property_create_object(dev, DRM_MODE_PROP_ATOMIC, 15802c9916cdSFrançois Tigeot "CRTC_ID", DRM_MODE_OBJECT_CRTC); 15812c9916cdSFrançois Tigeot if (!prop) 15822c9916cdSFrançois Tigeot return -ENOMEM; 15832c9916cdSFrançois Tigeot dev->mode_config.prop_crtc_id = prop; 15842c9916cdSFrançois Tigeot 15852c9916cdSFrançois Tigeot prop = drm_property_create_bool(dev, DRM_MODE_PROP_ATOMIC, 15862c9916cdSFrançois Tigeot "ACTIVE"); 15872c9916cdSFrançois Tigeot if (!prop) 15882c9916cdSFrançois Tigeot return -ENOMEM; 15892c9916cdSFrançois Tigeot dev->mode_config.prop_active = prop; 1590ba55f2f5SFrançois Tigeot 159119c468b4SFrançois Tigeot prop = drm_property_create(dev, 159219c468b4SFrançois Tigeot DRM_MODE_PROP_ATOMIC | DRM_MODE_PROP_BLOB, 159319c468b4SFrançois Tigeot "MODE_ID", 0); 159419c468b4SFrançois Tigeot if (!prop) 159519c468b4SFrançois Tigeot return -ENOMEM; 159619c468b4SFrançois Tigeot dev->mode_config.prop_mode_id = prop; 159719c468b4SFrançois Tigeot 1598c0e85e96SFrançois Tigeot prop = drm_property_create(dev, 1599c0e85e96SFrançois Tigeot DRM_MODE_PROP_BLOB, 1600c0e85e96SFrançois Tigeot "DEGAMMA_LUT", 0); 1601c0e85e96SFrançois Tigeot if (!prop) 1602c0e85e96SFrançois Tigeot return -ENOMEM; 1603c0e85e96SFrançois Tigeot dev->mode_config.degamma_lut_property = prop; 1604c0e85e96SFrançois Tigeot 1605c0e85e96SFrançois Tigeot prop = drm_property_create_range(dev, 1606c0e85e96SFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 1607c0e85e96SFrançois Tigeot "DEGAMMA_LUT_SIZE", 0, UINT_MAX); 1608c0e85e96SFrançois Tigeot if (!prop) 1609c0e85e96SFrançois Tigeot return -ENOMEM; 1610c0e85e96SFrançois Tigeot dev->mode_config.degamma_lut_size_property = prop; 1611c0e85e96SFrançois Tigeot 1612c0e85e96SFrançois Tigeot prop = drm_property_create(dev, 1613c0e85e96SFrançois Tigeot DRM_MODE_PROP_BLOB, 1614c0e85e96SFrançois Tigeot "CTM", 0); 1615c0e85e96SFrançois Tigeot if (!prop) 1616c0e85e96SFrançois Tigeot return -ENOMEM; 1617c0e85e96SFrançois Tigeot dev->mode_config.ctm_property = prop; 1618c0e85e96SFrançois Tigeot 1619c0e85e96SFrançois Tigeot prop = drm_property_create(dev, 1620c0e85e96SFrançois Tigeot DRM_MODE_PROP_BLOB, 1621c0e85e96SFrançois Tigeot "GAMMA_LUT", 0); 1622c0e85e96SFrançois Tigeot if (!prop) 1623c0e85e96SFrançois Tigeot return -ENOMEM; 1624c0e85e96SFrançois Tigeot dev->mode_config.gamma_lut_property = prop; 1625c0e85e96SFrançois Tigeot 1626c0e85e96SFrançois Tigeot prop = drm_property_create_range(dev, 1627c0e85e96SFrançois Tigeot DRM_MODE_PROP_IMMUTABLE, 1628c0e85e96SFrançois Tigeot "GAMMA_LUT_SIZE", 0, UINT_MAX); 1629c0e85e96SFrançois Tigeot if (!prop) 1630c0e85e96SFrançois Tigeot return -ENOMEM; 1631c0e85e96SFrançois Tigeot dev->mode_config.gamma_lut_size_property = prop; 1632c0e85e96SFrançois Tigeot 1633ba55f2f5SFrançois Tigeot return 0; 1634ba55f2f5SFrançois Tigeot } 1635ba55f2f5SFrançois Tigeot 16365718399fSFrançois Tigeot /** 16375718399fSFrançois Tigeot * drm_mode_create_dvi_i_properties - create DVI-I specific connector properties 16385718399fSFrançois Tigeot * @dev: DRM device 16395718399fSFrançois Tigeot * 16405718399fSFrançois Tigeot * Called by a driver the first time a DVI-I connector is made. 16415718399fSFrançois Tigeot */ 16425718399fSFrançois Tigeot int drm_mode_create_dvi_i_properties(struct drm_device *dev) 16435718399fSFrançois Tigeot { 16445718399fSFrançois Tigeot struct drm_property *dvi_i_selector; 16455718399fSFrançois Tigeot struct drm_property *dvi_i_subconnector; 16465718399fSFrançois Tigeot 16475718399fSFrançois Tigeot if (dev->mode_config.dvi_i_select_subconnector_property) 16485718399fSFrançois Tigeot return 0; 16495718399fSFrançois Tigeot 16505718399fSFrançois Tigeot dvi_i_selector = 16515718399fSFrançois Tigeot drm_property_create_enum(dev, 0, 16525718399fSFrançois Tigeot "select subconnector", 16535718399fSFrançois Tigeot drm_dvi_i_select_enum_list, 1654b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_select_enum_list)); 16555718399fSFrançois Tigeot dev->mode_config.dvi_i_select_subconnector_property = dvi_i_selector; 16565718399fSFrançois Tigeot 16575718399fSFrançois Tigeot dvi_i_subconnector = drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 16585718399fSFrançois Tigeot "subconnector", 16595718399fSFrançois Tigeot drm_dvi_i_subconnector_enum_list, 1660b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dvi_i_subconnector_enum_list)); 16615718399fSFrançois Tigeot dev->mode_config.dvi_i_subconnector_property = dvi_i_subconnector; 16625718399fSFrançois Tigeot 16635718399fSFrançois Tigeot return 0; 16645718399fSFrançois Tigeot } 1665b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dvi_i_properties); 16665718399fSFrançois Tigeot 16675718399fSFrançois Tigeot /** 16685718399fSFrançois Tigeot * drm_create_tv_properties - create TV specific connector properties 16695718399fSFrançois Tigeot * @dev: DRM device 16705718399fSFrançois Tigeot * @num_modes: number of different TV formats (modes) supported 16715718399fSFrançois Tigeot * @modes: array of pointers to strings containing name of each format 16725718399fSFrançois Tigeot * 16735718399fSFrançois Tigeot * Called by a driver's TV initialization routine, this function creates 16745718399fSFrançois Tigeot * the TV specific connector properties for a given device. Caller is 16755718399fSFrançois Tigeot * responsible for allocating a list of format names and passing them to 16765718399fSFrançois Tigeot * this routine. 16775718399fSFrançois Tigeot */ 16782c9916cdSFrançois Tigeot int drm_mode_create_tv_properties(struct drm_device *dev, 16792c9916cdSFrançois Tigeot unsigned int num_modes, 1680352ff8bdSFrançois Tigeot const char * const modes[]) 16815718399fSFrançois Tigeot { 16825718399fSFrançois Tigeot struct drm_property *tv_selector; 16835718399fSFrançois Tigeot struct drm_property *tv_subconnector; 16842c9916cdSFrançois Tigeot unsigned int i; 16855718399fSFrançois Tigeot 16865718399fSFrançois Tigeot if (dev->mode_config.tv_select_subconnector_property) 16875718399fSFrançois Tigeot return 0; 16885718399fSFrançois Tigeot 16895718399fSFrançois Tigeot /* 16905718399fSFrançois Tigeot * Basic connector properties 16915718399fSFrançois Tigeot */ 16925718399fSFrançois Tigeot tv_selector = drm_property_create_enum(dev, 0, 16935718399fSFrançois Tigeot "select subconnector", 16945718399fSFrançois Tigeot drm_tv_select_enum_list, 1695b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_select_enum_list)); 1696352ff8bdSFrançois Tigeot if (!tv_selector) 1697352ff8bdSFrançois Tigeot goto nomem; 1698352ff8bdSFrançois Tigeot 16995718399fSFrançois Tigeot dev->mode_config.tv_select_subconnector_property = tv_selector; 17005718399fSFrançois Tigeot 17015718399fSFrançois Tigeot tv_subconnector = 17025718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 17035718399fSFrançois Tigeot "subconnector", 17045718399fSFrançois Tigeot drm_tv_subconnector_enum_list, 1705b5162e19SFrançois Tigeot ARRAY_SIZE(drm_tv_subconnector_enum_list)); 1706352ff8bdSFrançois Tigeot if (!tv_subconnector) 1707352ff8bdSFrançois Tigeot goto nomem; 17085718399fSFrançois Tigeot dev->mode_config.tv_subconnector_property = tv_subconnector; 17095718399fSFrançois Tigeot 17105718399fSFrançois Tigeot /* 17115718399fSFrançois Tigeot * Other, TV specific properties: margins & TV modes. 17125718399fSFrançois Tigeot */ 17135718399fSFrançois Tigeot dev->mode_config.tv_left_margin_property = 17145718399fSFrançois Tigeot drm_property_create_range(dev, 0, "left margin", 0, 100); 1715352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_left_margin_property) 1716352ff8bdSFrançois Tigeot goto nomem; 17175718399fSFrançois Tigeot 17185718399fSFrançois Tigeot dev->mode_config.tv_right_margin_property = 17195718399fSFrançois Tigeot drm_property_create_range(dev, 0, "right margin", 0, 100); 1720352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_right_margin_property) 1721352ff8bdSFrançois Tigeot goto nomem; 17225718399fSFrançois Tigeot 17235718399fSFrançois Tigeot dev->mode_config.tv_top_margin_property = 17245718399fSFrançois Tigeot drm_property_create_range(dev, 0, "top margin", 0, 100); 1725352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_top_margin_property) 1726352ff8bdSFrançois Tigeot goto nomem; 17275718399fSFrançois Tigeot 17285718399fSFrançois Tigeot dev->mode_config.tv_bottom_margin_property = 17295718399fSFrançois Tigeot drm_property_create_range(dev, 0, "bottom margin", 0, 100); 1730352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_bottom_margin_property) 1731352ff8bdSFrançois Tigeot goto nomem; 17325718399fSFrançois Tigeot 17335718399fSFrançois Tigeot dev->mode_config.tv_mode_property = 17345718399fSFrançois Tigeot drm_property_create(dev, DRM_MODE_PROP_ENUM, 17355718399fSFrançois Tigeot "mode", num_modes); 1736352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_mode_property) 1737352ff8bdSFrançois Tigeot goto nomem; 1738352ff8bdSFrançois Tigeot 17395718399fSFrançois Tigeot for (i = 0; i < num_modes; i++) 17405718399fSFrançois Tigeot drm_property_add_enum(dev->mode_config.tv_mode_property, i, 17415718399fSFrançois Tigeot i, modes[i]); 17425718399fSFrançois Tigeot 17435718399fSFrançois Tigeot dev->mode_config.tv_brightness_property = 17445718399fSFrançois Tigeot drm_property_create_range(dev, 0, "brightness", 0, 100); 1745352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_brightness_property) 1746352ff8bdSFrançois Tigeot goto nomem; 17475718399fSFrançois Tigeot 17485718399fSFrançois Tigeot dev->mode_config.tv_contrast_property = 17495718399fSFrançois Tigeot drm_property_create_range(dev, 0, "contrast", 0, 100); 1750352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_contrast_property) 1751352ff8bdSFrançois Tigeot goto nomem; 17525718399fSFrançois Tigeot 17535718399fSFrançois Tigeot dev->mode_config.tv_flicker_reduction_property = 17545718399fSFrançois Tigeot drm_property_create_range(dev, 0, "flicker reduction", 0, 100); 1755352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_flicker_reduction_property) 1756352ff8bdSFrançois Tigeot goto nomem; 17575718399fSFrançois Tigeot 17585718399fSFrançois Tigeot dev->mode_config.tv_overscan_property = 17595718399fSFrançois Tigeot drm_property_create_range(dev, 0, "overscan", 0, 100); 1760352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_overscan_property) 1761352ff8bdSFrançois Tigeot goto nomem; 17625718399fSFrançois Tigeot 17635718399fSFrançois Tigeot dev->mode_config.tv_saturation_property = 17645718399fSFrançois Tigeot drm_property_create_range(dev, 0, "saturation", 0, 100); 1765352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_saturation_property) 1766352ff8bdSFrançois Tigeot goto nomem; 17675718399fSFrançois Tigeot 17685718399fSFrançois Tigeot dev->mode_config.tv_hue_property = 17695718399fSFrançois Tigeot drm_property_create_range(dev, 0, "hue", 0, 100); 1770352ff8bdSFrançois Tigeot if (!dev->mode_config.tv_hue_property) 1771352ff8bdSFrançois Tigeot goto nomem; 17725718399fSFrançois Tigeot 17735718399fSFrançois Tigeot return 0; 1774352ff8bdSFrançois Tigeot nomem: 1775352ff8bdSFrançois Tigeot return -ENOMEM; 17765718399fSFrançois Tigeot } 1777b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tv_properties); 17785718399fSFrançois Tigeot 17795718399fSFrançois Tigeot /** 17805718399fSFrançois Tigeot * drm_mode_create_scaling_mode_property - create scaling mode property 17815718399fSFrançois Tigeot * @dev: DRM device 17825718399fSFrançois Tigeot * 17835718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 17845718399fSFrançois Tigeot * connectors. 17855718399fSFrançois Tigeot */ 17865718399fSFrançois Tigeot int drm_mode_create_scaling_mode_property(struct drm_device *dev) 17875718399fSFrançois Tigeot { 17885718399fSFrançois Tigeot struct drm_property *scaling_mode; 17895718399fSFrançois Tigeot 17905718399fSFrançois Tigeot if (dev->mode_config.scaling_mode_property) 17915718399fSFrançois Tigeot return 0; 17925718399fSFrançois Tigeot 17935718399fSFrançois Tigeot scaling_mode = 17945718399fSFrançois Tigeot drm_property_create_enum(dev, 0, "scaling mode", 17955718399fSFrançois Tigeot drm_scaling_mode_enum_list, 1796b5162e19SFrançois Tigeot ARRAY_SIZE(drm_scaling_mode_enum_list)); 17975718399fSFrançois Tigeot 17985718399fSFrançois Tigeot dev->mode_config.scaling_mode_property = scaling_mode; 17995718399fSFrançois Tigeot 18005718399fSFrançois Tigeot return 0; 18015718399fSFrançois Tigeot } 1802b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_scaling_mode_property); 18035718399fSFrançois Tigeot 18045718399fSFrançois Tigeot /** 180524edb884SFrançois Tigeot * drm_mode_create_aspect_ratio_property - create aspect ratio property 180624edb884SFrançois Tigeot * @dev: DRM device 180724edb884SFrançois Tigeot * 180824edb884SFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 180924edb884SFrançois Tigeot * connectors. 181024edb884SFrançois Tigeot * 181124edb884SFrançois Tigeot * Returns: 18122c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 181324edb884SFrançois Tigeot */ 181424edb884SFrançois Tigeot int drm_mode_create_aspect_ratio_property(struct drm_device *dev) 181524edb884SFrançois Tigeot { 181624edb884SFrançois Tigeot if (dev->mode_config.aspect_ratio_property) 181724edb884SFrançois Tigeot return 0; 181824edb884SFrançois Tigeot 181924edb884SFrançois Tigeot dev->mode_config.aspect_ratio_property = 182024edb884SFrançois Tigeot drm_property_create_enum(dev, 0, "aspect ratio", 182124edb884SFrançois Tigeot drm_aspect_ratio_enum_list, 182224edb884SFrançois Tigeot ARRAY_SIZE(drm_aspect_ratio_enum_list)); 182324edb884SFrançois Tigeot 182424edb884SFrançois Tigeot if (dev->mode_config.aspect_ratio_property == NULL) 182524edb884SFrançois Tigeot return -ENOMEM; 182624edb884SFrançois Tigeot 182724edb884SFrançois Tigeot return 0; 182824edb884SFrançois Tigeot } 182924edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_aspect_ratio_property); 183024edb884SFrançois Tigeot 183124edb884SFrançois Tigeot /** 18325718399fSFrançois Tigeot * drm_mode_create_dirty_property - create dirty property 18335718399fSFrançois Tigeot * @dev: DRM device 18345718399fSFrançois Tigeot * 18355718399fSFrançois Tigeot * Called by a driver the first time it's needed, must be attached to desired 18365718399fSFrançois Tigeot * connectors. 18375718399fSFrançois Tigeot */ 18385718399fSFrançois Tigeot int drm_mode_create_dirty_info_property(struct drm_device *dev) 18395718399fSFrançois Tigeot { 18405718399fSFrançois Tigeot struct drm_property *dirty_info; 18415718399fSFrançois Tigeot 18425718399fSFrançois Tigeot if (dev->mode_config.dirty_info_property) 18435718399fSFrançois Tigeot return 0; 18445718399fSFrançois Tigeot 18455718399fSFrançois Tigeot dirty_info = 18465718399fSFrançois Tigeot drm_property_create_enum(dev, DRM_MODE_PROP_IMMUTABLE, 18475718399fSFrançois Tigeot "dirty", 18485718399fSFrançois Tigeot drm_dirty_info_enum_list, 1849b5162e19SFrançois Tigeot ARRAY_SIZE(drm_dirty_info_enum_list)); 18505718399fSFrançois Tigeot dev->mode_config.dirty_info_property = dirty_info; 18515718399fSFrançois Tigeot 18525718399fSFrançois Tigeot return 0; 18535718399fSFrançois Tigeot } 1854b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_dirty_info_property); 18555718399fSFrançois Tigeot 18562c9916cdSFrançois Tigeot /** 18572c9916cdSFrançois Tigeot * drm_mode_create_suggested_offset_properties - create suggests offset properties 18582c9916cdSFrançois Tigeot * @dev: DRM device 18592c9916cdSFrançois Tigeot * 18602c9916cdSFrançois Tigeot * Create the the suggested x/y offset property for connectors. 18612c9916cdSFrançois Tigeot */ 18622c9916cdSFrançois Tigeot int drm_mode_create_suggested_offset_properties(struct drm_device *dev) 18632c9916cdSFrançois Tigeot { 18642c9916cdSFrançois Tigeot if (dev->mode_config.suggested_x_property && dev->mode_config.suggested_y_property) 18652c9916cdSFrançois Tigeot return 0; 18662c9916cdSFrançois Tigeot 18672c9916cdSFrançois Tigeot dev->mode_config.suggested_x_property = 18682c9916cdSFrançois Tigeot drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested X", 0, 0xffffffff); 18692c9916cdSFrançois Tigeot 18702c9916cdSFrançois Tigeot dev->mode_config.suggested_y_property = 18712c9916cdSFrançois Tigeot drm_property_create_range(dev, DRM_MODE_PROP_IMMUTABLE, "suggested Y", 0, 0xffffffff); 18722c9916cdSFrançois Tigeot 18732c9916cdSFrançois Tigeot if (dev->mode_config.suggested_x_property == NULL || 18742c9916cdSFrançois Tigeot dev->mode_config.suggested_y_property == NULL) 18752c9916cdSFrançois Tigeot return -ENOMEM; 18762c9916cdSFrançois Tigeot return 0; 18772c9916cdSFrançois Tigeot } 18782c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_suggested_offset_properties); 18792c9916cdSFrançois Tigeot 18805718399fSFrançois Tigeot /** 18815718399fSFrançois Tigeot * drm_mode_getresources - get graphics configuration 18824dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 18834dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 18844dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 18855718399fSFrançois Tigeot * 18865718399fSFrançois Tigeot * Construct a set of configuration description structures and return 18875718399fSFrançois Tigeot * them to the user, including CRTC, connector and framebuffer configuration. 18885718399fSFrançois Tigeot * 18895718399fSFrançois Tigeot * Called by the user via ioctl. 18905718399fSFrançois Tigeot * 1891ba55f2f5SFrançois Tigeot * Returns: 18922c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 18935718399fSFrançois Tigeot */ 18945718399fSFrançois Tigeot int drm_mode_getresources(struct drm_device *dev, void *data, 18955718399fSFrançois Tigeot struct drm_file *file_priv) 18965718399fSFrançois Tigeot { 18975718399fSFrançois Tigeot struct drm_mode_card_res *card_res = data; 18985718399fSFrançois Tigeot struct list_head *lh; 18995718399fSFrançois Tigeot struct drm_framebuffer *fb; 19005718399fSFrançois Tigeot struct drm_connector *connector; 19015718399fSFrançois Tigeot struct drm_crtc *crtc; 19025718399fSFrançois Tigeot struct drm_encoder *encoder; 19035718399fSFrançois Tigeot int ret = 0; 19045718399fSFrançois Tigeot int connector_count = 0; 19055718399fSFrançois Tigeot int crtc_count = 0; 19065718399fSFrançois Tigeot int fb_count = 0; 19075718399fSFrançois Tigeot int encoder_count = 0; 1908a05eeebfSFrançois Tigeot int copied = 0; 19095718399fSFrançois Tigeot uint32_t __user *fb_id; 19105718399fSFrançois Tigeot uint32_t __user *crtc_id; 19115718399fSFrançois Tigeot uint32_t __user *connector_id; 19125718399fSFrançois Tigeot uint32_t __user *encoder_id; 19135718399fSFrançois Tigeot 19145718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 1915b5162e19SFrançois Tigeot return -EINVAL; 19165718399fSFrançois Tigeot 19175718399fSFrançois Tigeot 19184dbb207bSFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 19195718399fSFrançois Tigeot /* 19205718399fSFrançois Tigeot * For the non-control nodes we need to limit the list of resources 19215718399fSFrançois Tigeot * by IDs in the group list for this node 19225718399fSFrançois Tigeot */ 19235718399fSFrançois Tigeot list_for_each(lh, &file_priv->fbs) 19245718399fSFrançois Tigeot fb_count++; 19255718399fSFrançois Tigeot 19264dbb207bSFrançois Tigeot /* handle this in 4 parts */ 19274dbb207bSFrançois Tigeot /* FBs */ 19284dbb207bSFrançois Tigeot if (card_res->count_fbs >= fb_count) { 19294dbb207bSFrançois Tigeot copied = 0; 19304dbb207bSFrançois Tigeot fb_id = (uint32_t __user *)(unsigned long)card_res->fb_id_ptr; 19314dbb207bSFrançois Tigeot list_for_each_entry(fb, &file_priv->fbs, filp_head) { 19324dbb207bSFrançois Tigeot if (put_user(fb->base.id, fb_id + copied)) { 19334dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 19344dbb207bSFrançois Tigeot return -EFAULT; 19354dbb207bSFrançois Tigeot } 19364dbb207bSFrançois Tigeot copied++; 19374dbb207bSFrançois Tigeot } 19384dbb207bSFrançois Tigeot } 19394dbb207bSFrançois Tigeot card_res->count_fbs = fb_count; 19404dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 19414dbb207bSFrançois Tigeot 19422c9916cdSFrançois Tigeot /* mode_config.mutex protects the connector list against e.g. DP MST 19432c9916cdSFrançois Tigeot * connector hot-adding. CRTC/Plane lists are invariant. */ 19442c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 1945a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) 19465718399fSFrançois Tigeot crtc_count++; 19475718399fSFrançois Tigeot 1948a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) 19495718399fSFrançois Tigeot connector_count++; 19505718399fSFrançois Tigeot 1951a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) 19525718399fSFrançois Tigeot encoder_count++; 19535718399fSFrançois Tigeot 19545718399fSFrançois Tigeot card_res->max_height = dev->mode_config.max_height; 19555718399fSFrançois Tigeot card_res->min_height = dev->mode_config.min_height; 19565718399fSFrançois Tigeot card_res->max_width = dev->mode_config.max_width; 19575718399fSFrançois Tigeot card_res->min_width = dev->mode_config.min_width; 19585718399fSFrançois Tigeot 19595718399fSFrançois Tigeot /* CRTCs */ 19605718399fSFrançois Tigeot if (card_res->count_crtcs >= crtc_count) { 19615718399fSFrançois Tigeot copied = 0; 1962b5162e19SFrançois Tigeot crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr; 1963a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) { 1964b5162e19SFrançois Tigeot if (put_user(crtc->base.id, crtc_id + copied)) { 1965b5162e19SFrançois Tigeot ret = -EFAULT; 19665718399fSFrançois Tigeot goto out; 19675718399fSFrançois Tigeot } 19685718399fSFrançois Tigeot copied++; 19695718399fSFrançois Tigeot } 19705718399fSFrançois Tigeot } 19715718399fSFrançois Tigeot card_res->count_crtcs = crtc_count; 19725718399fSFrançois Tigeot 19735718399fSFrançois Tigeot /* Encoders */ 19745718399fSFrançois Tigeot if (card_res->count_encoders >= encoder_count) { 19755718399fSFrançois Tigeot copied = 0; 1976b5162e19SFrançois Tigeot encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr; 1977a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) { 1978b5162e19SFrançois Tigeot if (put_user(encoder->base.id, encoder_id + 1979b5162e19SFrançois Tigeot copied)) { 1980b5162e19SFrançois Tigeot ret = -EFAULT; 19815718399fSFrançois Tigeot goto out; 19825718399fSFrançois Tigeot } 19835718399fSFrançois Tigeot copied++; 19845718399fSFrançois Tigeot } 19855718399fSFrançois Tigeot } 19865718399fSFrançois Tigeot card_res->count_encoders = encoder_count; 19875718399fSFrançois Tigeot 19885718399fSFrançois Tigeot /* Connectors */ 19895718399fSFrançois Tigeot if (card_res->count_connectors >= connector_count) { 19905718399fSFrançois Tigeot copied = 0; 1991b5162e19SFrançois Tigeot connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr; 1992a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 1993b5162e19SFrançois Tigeot if (put_user(connector->base.id, 1994b5162e19SFrançois Tigeot connector_id + copied)) { 1995b5162e19SFrançois Tigeot ret = -EFAULT; 19965718399fSFrançois Tigeot goto out; 19975718399fSFrançois Tigeot } 19985718399fSFrançois Tigeot copied++; 19995718399fSFrançois Tigeot } 20005718399fSFrançois Tigeot } 20015718399fSFrançois Tigeot card_res->count_connectors = connector_count; 20025718399fSFrançois Tigeot 20035718399fSFrançois Tigeot out: 20042c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 20055718399fSFrançois Tigeot return ret; 20065718399fSFrançois Tigeot } 20075718399fSFrançois Tigeot 20085718399fSFrançois Tigeot /** 20095718399fSFrançois Tigeot * drm_mode_getcrtc - get CRTC configuration 20104dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 20114dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 20124dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 20135718399fSFrançois Tigeot * 20145718399fSFrançois Tigeot * Construct a CRTC configuration structure to return to the user. 20155718399fSFrançois Tigeot * 20165718399fSFrançois Tigeot * Called by the user via ioctl. 20175718399fSFrançois Tigeot * 2018ba55f2f5SFrançois Tigeot * Returns: 20192c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 20205718399fSFrançois Tigeot */ 20215718399fSFrançois Tigeot int drm_mode_getcrtc(struct drm_device *dev, 20225718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 20235718399fSFrançois Tigeot { 20245718399fSFrançois Tigeot struct drm_mode_crtc *crtc_resp = data; 20255718399fSFrançois Tigeot struct drm_crtc *crtc; 20265718399fSFrançois Tigeot 20275718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2028b5162e19SFrançois Tigeot return -EINVAL; 20295718399fSFrançois Tigeot 2030ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_resp->crtc_id); 20312c9916cdSFrançois Tigeot if (!crtc) 20322c9916cdSFrançois Tigeot return -ENOENT; 20335718399fSFrançois Tigeot 20342c9916cdSFrançois Tigeot drm_modeset_lock_crtc(crtc, crtc->primary); 20355718399fSFrançois Tigeot crtc_resp->gamma_size = crtc->gamma_size; 2036ba55f2f5SFrançois Tigeot if (crtc->primary->fb) 2037ba55f2f5SFrançois Tigeot crtc_resp->fb_id = crtc->primary->fb->base.id; 20385718399fSFrançois Tigeot else 20395718399fSFrançois Tigeot crtc_resp->fb_id = 0; 20405718399fSFrançois Tigeot 2041477eb7f9SFrançois Tigeot if (crtc->state) { 2042477eb7f9SFrançois Tigeot crtc_resp->x = crtc->primary->state->src_x >> 16; 2043477eb7f9SFrançois Tigeot crtc_resp->y = crtc->primary->state->src_y >> 16; 2044477eb7f9SFrançois Tigeot if (crtc->state->enable) { 204519c468b4SFrançois Tigeot drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->state->mode); 2046477eb7f9SFrançois Tigeot crtc_resp->mode_valid = 1; 20475718399fSFrançois Tigeot 2048477eb7f9SFrançois Tigeot } else { 2049477eb7f9SFrançois Tigeot crtc_resp->mode_valid = 0; 2050477eb7f9SFrançois Tigeot } 2051477eb7f9SFrançois Tigeot } else { 2052477eb7f9SFrançois Tigeot crtc_resp->x = crtc->x; 2053477eb7f9SFrançois Tigeot crtc_resp->y = crtc->y; 2054477eb7f9SFrançois Tigeot if (crtc->enabled) { 205519c468b4SFrançois Tigeot drm_mode_convert_to_umode(&crtc_resp->mode, &crtc->mode); 20565718399fSFrançois Tigeot crtc_resp->mode_valid = 1; 20575718399fSFrançois Tigeot 20585718399fSFrançois Tigeot } else { 20595718399fSFrançois Tigeot crtc_resp->mode_valid = 0; 20605718399fSFrançois Tigeot } 2061477eb7f9SFrançois Tigeot } 20622c9916cdSFrançois Tigeot drm_modeset_unlock_crtc(crtc); 20635718399fSFrançois Tigeot 20642c9916cdSFrançois Tigeot return 0; 20655718399fSFrançois Tigeot } 20665718399fSFrançois Tigeot 20679edbd4a0SFrançois Tigeot static bool drm_mode_expose_to_userspace(const struct drm_display_mode *mode, 20689edbd4a0SFrançois Tigeot const struct drm_file *file_priv) 20699edbd4a0SFrançois Tigeot { 20709edbd4a0SFrançois Tigeot /* 20719edbd4a0SFrançois Tigeot * If user-space hasn't configured the driver to expose the stereo 3D 20729edbd4a0SFrançois Tigeot * modes, don't expose them. 20739edbd4a0SFrançois Tigeot */ 20749edbd4a0SFrançois Tigeot if (!file_priv->stereo_allowed && drm_mode_is_stereo(mode)) 20759edbd4a0SFrançois Tigeot return false; 20769edbd4a0SFrançois Tigeot 20779edbd4a0SFrançois Tigeot return true; 20789edbd4a0SFrançois Tigeot } 20799edbd4a0SFrançois Tigeot 20802c9916cdSFrançois Tigeot static struct drm_encoder *drm_connector_get_encoder(struct drm_connector *connector) 20812c9916cdSFrançois Tigeot { 20822c9916cdSFrançois Tigeot /* For atomic drivers only state objects are synchronously updated and 20832c9916cdSFrançois Tigeot * protected by modeset locks, so check those first. */ 20842c9916cdSFrançois Tigeot if (connector->state) 20852c9916cdSFrançois Tigeot return connector->state->best_encoder; 20862c9916cdSFrançois Tigeot return connector->encoder; 20872c9916cdSFrançois Tigeot } 20882c9916cdSFrançois Tigeot 20892c9916cdSFrançois Tigeot /* helper for getconnector and getproperties ioctls */ 20902c9916cdSFrançois Tigeot static int get_properties(struct drm_mode_object *obj, bool atomic, 20912c9916cdSFrançois Tigeot uint32_t __user *prop_ptr, uint64_t __user *prop_values, 20922c9916cdSFrançois Tigeot uint32_t *arg_count_props) 20932c9916cdSFrançois Tigeot { 20942c9916cdSFrançois Tigeot int props_count; 20952c9916cdSFrançois Tigeot int i, ret, copied; 20962c9916cdSFrançois Tigeot 20972c9916cdSFrançois Tigeot props_count = obj->properties->count; 20982c9916cdSFrançois Tigeot if (!atomic) 20992c9916cdSFrançois Tigeot props_count -= obj->properties->atomic_count; 21002c9916cdSFrançois Tigeot 21012c9916cdSFrançois Tigeot if ((*arg_count_props >= props_count) && props_count) { 21022c9916cdSFrançois Tigeot for (i = 0, copied = 0; copied < props_count; i++) { 21032c9916cdSFrançois Tigeot struct drm_property *prop = obj->properties->properties[i]; 21042c9916cdSFrançois Tigeot uint64_t val; 21052c9916cdSFrançois Tigeot 21062c9916cdSFrançois Tigeot if ((prop->flags & DRM_MODE_PROP_ATOMIC) && !atomic) 21072c9916cdSFrançois Tigeot continue; 21082c9916cdSFrançois Tigeot 21092c9916cdSFrançois Tigeot ret = drm_object_property_get_value(obj, prop, &val); 21102c9916cdSFrançois Tigeot if (ret) 21112c9916cdSFrançois Tigeot return ret; 21122c9916cdSFrançois Tigeot 21132c9916cdSFrançois Tigeot if (put_user(prop->base.id, prop_ptr + copied)) 21142c9916cdSFrançois Tigeot return -EFAULT; 21152c9916cdSFrançois Tigeot 21162c9916cdSFrançois Tigeot if (put_user(val, prop_values + copied)) 21172c9916cdSFrançois Tigeot return -EFAULT; 21182c9916cdSFrançois Tigeot 21192c9916cdSFrançois Tigeot copied++; 21202c9916cdSFrançois Tigeot } 21212c9916cdSFrançois Tigeot } 21222c9916cdSFrançois Tigeot *arg_count_props = props_count; 21232c9916cdSFrançois Tigeot 21242c9916cdSFrançois Tigeot return 0; 21252c9916cdSFrançois Tigeot } 21262c9916cdSFrançois Tigeot 21275718399fSFrançois Tigeot /** 21285718399fSFrançois Tigeot * drm_mode_getconnector - get connector configuration 21294dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 21304dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 21314dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 21325718399fSFrançois Tigeot * 21335718399fSFrançois Tigeot * Construct a connector configuration structure to return to the user. 21345718399fSFrançois Tigeot * 21355718399fSFrançois Tigeot * Called by the user via ioctl. 21365718399fSFrançois Tigeot * 2137ba55f2f5SFrançois Tigeot * Returns: 21382c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 21395718399fSFrançois Tigeot */ 21405718399fSFrançois Tigeot int drm_mode_getconnector(struct drm_device *dev, void *data, 21415718399fSFrançois Tigeot struct drm_file *file_priv) 21425718399fSFrançois Tigeot { 21435718399fSFrançois Tigeot struct drm_mode_get_connector *out_resp = data; 21445718399fSFrançois Tigeot struct drm_connector *connector; 21452c9916cdSFrançois Tigeot struct drm_encoder *encoder; 21465718399fSFrançois Tigeot struct drm_display_mode *mode; 21475718399fSFrançois Tigeot int mode_count = 0; 21485718399fSFrançois Tigeot int encoders_count = 0; 21495718399fSFrançois Tigeot int ret = 0; 21505718399fSFrançois Tigeot int copied = 0; 21515718399fSFrançois Tigeot int i; 21525718399fSFrançois Tigeot struct drm_mode_modeinfo u_mode; 21535718399fSFrançois Tigeot struct drm_mode_modeinfo __user *mode_ptr; 2154b5162e19SFrançois Tigeot uint32_t __user *encoder_ptr; 21555718399fSFrançois Tigeot 21565718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2157b5162e19SFrançois Tigeot return -EINVAL; 21585718399fSFrançois Tigeot 21595718399fSFrançois Tigeot memset(&u_mode, 0, sizeof(struct drm_mode_modeinfo)); 21605718399fSFrançois Tigeot 21614dbb207bSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 21625718399fSFrançois Tigeot 21638621f407SFrançois Tigeot connector = drm_connector_lookup(dev, out_resp->connector_id); 2164ba55f2f5SFrançois Tigeot if (!connector) { 21659edbd4a0SFrançois Tigeot ret = -ENOENT; 21662c9916cdSFrançois Tigeot goto out_unlock; 21675718399fSFrançois Tigeot } 21685718399fSFrançois Tigeot 21692c9916cdSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) 21702c9916cdSFrançois Tigeot if (connector->encoder_ids[i] != 0) 21715718399fSFrançois Tigeot encoders_count++; 21725718399fSFrançois Tigeot 21735718399fSFrançois Tigeot if (out_resp->count_modes == 0) { 21745718399fSFrançois Tigeot connector->funcs->fill_modes(connector, 21755718399fSFrançois Tigeot dev->mode_config.max_width, 21765718399fSFrançois Tigeot dev->mode_config.max_height); 21775718399fSFrançois Tigeot } 21785718399fSFrançois Tigeot 21795718399fSFrançois Tigeot /* delayed so we get modes regardless of pre-fill_modes state */ 21805718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) 21819edbd4a0SFrançois Tigeot if (drm_mode_expose_to_userspace(mode, file_priv)) 21825718399fSFrançois Tigeot mode_count++; 21835718399fSFrançois Tigeot 21845718399fSFrançois Tigeot out_resp->connector_id = connector->base.id; 21855718399fSFrançois Tigeot out_resp->connector_type = connector->connector_type; 21865718399fSFrançois Tigeot out_resp->connector_type_id = connector->connector_type_id; 21875718399fSFrançois Tigeot out_resp->mm_width = connector->display_info.width_mm; 21885718399fSFrançois Tigeot out_resp->mm_height = connector->display_info.height_mm; 21895718399fSFrançois Tigeot out_resp->subpixel = connector->display_info.subpixel_order; 21905718399fSFrançois Tigeot out_resp->connection = connector->status; 21912c9916cdSFrançois Tigeot 21922c9916cdSFrançois Tigeot drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 21932c9916cdSFrançois Tigeot encoder = drm_connector_get_encoder(connector); 21942c9916cdSFrançois Tigeot if (encoder) 21952c9916cdSFrançois Tigeot out_resp->encoder_id = encoder->base.id; 21965718399fSFrançois Tigeot else 21975718399fSFrançois Tigeot out_resp->encoder_id = 0; 21985718399fSFrançois Tigeot 21995718399fSFrançois Tigeot /* 22005718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 22015718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 22025718399fSFrançois Tigeot */ 22035718399fSFrançois Tigeot if ((out_resp->count_modes >= mode_count) && mode_count) { 22045718399fSFrançois Tigeot copied = 0; 2205b5162e19SFrançois Tigeot mode_ptr = (struct drm_mode_modeinfo __user *)(unsigned long)out_resp->modes_ptr; 22065718399fSFrançois Tigeot list_for_each_entry(mode, &connector->modes, head) { 22079edbd4a0SFrançois Tigeot if (!drm_mode_expose_to_userspace(mode, file_priv)) 22089edbd4a0SFrançois Tigeot continue; 22099edbd4a0SFrançois Tigeot 221019c468b4SFrançois Tigeot drm_mode_convert_to_umode(&u_mode, mode); 2211b5162e19SFrançois Tigeot if (copy_to_user(mode_ptr + copied, 2212b5162e19SFrançois Tigeot &u_mode, sizeof(u_mode))) { 2213b5162e19SFrançois Tigeot ret = -EFAULT; 22145718399fSFrançois Tigeot goto out; 22155718399fSFrançois Tigeot } 22165718399fSFrançois Tigeot copied++; 22175718399fSFrançois Tigeot } 22185718399fSFrançois Tigeot } 22195718399fSFrançois Tigeot out_resp->count_modes = mode_count; 22205718399fSFrançois Tigeot 22212c9916cdSFrançois Tigeot ret = get_properties(&connector->base, file_priv->atomic, 22222c9916cdSFrançois Tigeot (uint32_t __user *)(unsigned long)(out_resp->props_ptr), 22232c9916cdSFrançois Tigeot (uint64_t __user *)(unsigned long)(out_resp->prop_values_ptr), 22242c9916cdSFrançois Tigeot &out_resp->count_props); 22252c9916cdSFrançois Tigeot if (ret) 22265718399fSFrançois Tigeot goto out; 22275718399fSFrançois Tigeot 22285718399fSFrançois Tigeot if ((out_resp->count_encoders >= encoders_count) && encoders_count) { 22295718399fSFrançois Tigeot copied = 0; 2230b5162e19SFrançois Tigeot encoder_ptr = (uint32_t __user *)(unsigned long)(out_resp->encoders_ptr); 22315718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 22325718399fSFrançois Tigeot if (connector->encoder_ids[i] != 0) { 2233b5162e19SFrançois Tigeot if (put_user(connector->encoder_ids[i], 2234b5162e19SFrançois Tigeot encoder_ptr + copied)) { 2235b5162e19SFrançois Tigeot ret = -EFAULT; 22365718399fSFrançois Tigeot goto out; 22375718399fSFrançois Tigeot } 22385718399fSFrançois Tigeot copied++; 22395718399fSFrançois Tigeot } 22405718399fSFrançois Tigeot } 22415718399fSFrançois Tigeot } 22425718399fSFrançois Tigeot out_resp->count_encoders = encoders_count; 22435718399fSFrançois Tigeot 22445718399fSFrançois Tigeot out: 2245ba55f2f5SFrançois Tigeot drm_modeset_unlock(&dev->mode_config.connection_mutex); 22462c9916cdSFrançois Tigeot 22478621f407SFrançois Tigeot drm_connector_unreference(connector); 22482c9916cdSFrançois Tigeot out_unlock: 22494dbb207bSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 22504dbb207bSFrançois Tigeot 22515718399fSFrançois Tigeot return ret; 22525718399fSFrançois Tigeot } 22535718399fSFrançois Tigeot 22542c9916cdSFrançois Tigeot static struct drm_crtc *drm_encoder_get_crtc(struct drm_encoder *encoder) 22552c9916cdSFrançois Tigeot { 22562c9916cdSFrançois Tigeot struct drm_connector *connector; 22572c9916cdSFrançois Tigeot struct drm_device *dev = encoder->dev; 22582c9916cdSFrançois Tigeot bool uses_atomic = false; 22592c9916cdSFrançois Tigeot 22602c9916cdSFrançois Tigeot /* For atomic drivers only state objects are synchronously updated and 22612c9916cdSFrançois Tigeot * protected by modeset locks, so check those first. */ 2262a05eeebfSFrançois Tigeot drm_for_each_connector(connector, dev) { 22632c9916cdSFrançois Tigeot if (!connector->state) 22642c9916cdSFrançois Tigeot continue; 22652c9916cdSFrançois Tigeot 22662c9916cdSFrançois Tigeot uses_atomic = true; 22672c9916cdSFrançois Tigeot 22682c9916cdSFrançois Tigeot if (connector->state->best_encoder != encoder) 22692c9916cdSFrançois Tigeot continue; 22702c9916cdSFrançois Tigeot 22712c9916cdSFrançois Tigeot return connector->state->crtc; 22722c9916cdSFrançois Tigeot } 22732c9916cdSFrançois Tigeot 22742c9916cdSFrançois Tigeot /* Don't return stale data (e.g. pending async disable). */ 22752c9916cdSFrançois Tigeot if (uses_atomic) 22762c9916cdSFrançois Tigeot return NULL; 22772c9916cdSFrançois Tigeot 22782c9916cdSFrançois Tigeot return encoder->crtc; 22792c9916cdSFrançois Tigeot } 22802c9916cdSFrançois Tigeot 2281ba55f2f5SFrançois Tigeot /** 2282ba55f2f5SFrançois Tigeot * drm_mode_getencoder - get encoder configuration 2283ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 2284ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 2285ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 2286ba55f2f5SFrançois Tigeot * 2287ba55f2f5SFrançois Tigeot * Construct a encoder configuration structure to return to the user. 2288ba55f2f5SFrançois Tigeot * 2289ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2290ba55f2f5SFrançois Tigeot * 2291ba55f2f5SFrançois Tigeot * Returns: 22922c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 2293ba55f2f5SFrançois Tigeot */ 22945718399fSFrançois Tigeot int drm_mode_getencoder(struct drm_device *dev, void *data, 22955718399fSFrançois Tigeot struct drm_file *file_priv) 22965718399fSFrançois Tigeot { 22975718399fSFrançois Tigeot struct drm_mode_get_encoder *enc_resp = data; 22985718399fSFrançois Tigeot struct drm_encoder *encoder; 22992c9916cdSFrançois Tigeot struct drm_crtc *crtc; 23005718399fSFrançois Tigeot 23015718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2302b5162e19SFrançois Tigeot return -EINVAL; 23035718399fSFrançois Tigeot 2304ba55f2f5SFrançois Tigeot encoder = drm_encoder_find(dev, enc_resp->encoder_id); 23052c9916cdSFrançois Tigeot if (!encoder) 23062c9916cdSFrançois Tigeot return -ENOENT; 23075718399fSFrançois Tigeot 23082c9916cdSFrançois Tigeot drm_modeset_lock(&dev->mode_config.connection_mutex, NULL); 23092c9916cdSFrançois Tigeot crtc = drm_encoder_get_crtc(encoder); 23102c9916cdSFrançois Tigeot if (crtc) 23112c9916cdSFrançois Tigeot enc_resp->crtc_id = crtc->base.id; 23125718399fSFrançois Tigeot else 23135718399fSFrançois Tigeot enc_resp->crtc_id = 0; 23142c9916cdSFrançois Tigeot drm_modeset_unlock(&dev->mode_config.connection_mutex); 23152c9916cdSFrançois Tigeot 23165718399fSFrançois Tigeot enc_resp->encoder_type = encoder->encoder_type; 23175718399fSFrançois Tigeot enc_resp->encoder_id = encoder->base.id; 23185718399fSFrançois Tigeot enc_resp->possible_crtcs = encoder->possible_crtcs; 23195718399fSFrançois Tigeot enc_resp->possible_clones = encoder->possible_clones; 23205718399fSFrançois Tigeot 23212c9916cdSFrançois Tigeot return 0; 23225718399fSFrançois Tigeot } 23235718399fSFrançois Tigeot 23245718399fSFrançois Tigeot /** 2325ba55f2f5SFrançois Tigeot * drm_mode_getplane_res - enumerate all plane resources 23265718399fSFrançois Tigeot * @dev: DRM device 23275718399fSFrançois Tigeot * @data: ioctl data 23285718399fSFrançois Tigeot * @file_priv: DRM file info 23295718399fSFrançois Tigeot * 2330ba55f2f5SFrançois Tigeot * Construct a list of plane ids to return to the user. 2331ba55f2f5SFrançois Tigeot * 2332ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2333ba55f2f5SFrançois Tigeot * 2334ba55f2f5SFrançois Tigeot * Returns: 23352c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 23365718399fSFrançois Tigeot */ 23375718399fSFrançois Tigeot int drm_mode_getplane_res(struct drm_device *dev, void *data, 23385718399fSFrançois Tigeot struct drm_file *file_priv) 23395718399fSFrançois Tigeot { 23405718399fSFrançois Tigeot struct drm_mode_get_plane_res *plane_resp = data; 23415718399fSFrançois Tigeot struct drm_mode_config *config; 23425718399fSFrançois Tigeot struct drm_plane *plane; 2343b5162e19SFrançois Tigeot uint32_t __user *plane_ptr; 23442c9916cdSFrançois Tigeot int copied = 0; 2345ba55f2f5SFrançois Tigeot unsigned num_planes; 23465718399fSFrançois Tigeot 23475718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2348b5162e19SFrançois Tigeot return -EINVAL; 23495718399fSFrançois Tigeot 23505718399fSFrançois Tigeot config = &dev->mode_config; 23515718399fSFrançois Tigeot 2352ba55f2f5SFrançois Tigeot if (file_priv->universal_planes) 2353ba55f2f5SFrançois Tigeot num_planes = config->num_total_plane; 2354ba55f2f5SFrançois Tigeot else 2355ba55f2f5SFrançois Tigeot num_planes = config->num_overlay_plane; 2356ba55f2f5SFrançois Tigeot 23575718399fSFrançois Tigeot /* 23585718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 23595718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 23605718399fSFrançois Tigeot */ 2361ba55f2f5SFrançois Tigeot if (num_planes && 2362ba55f2f5SFrançois Tigeot (plane_resp->count_planes >= num_planes)) { 2363b5162e19SFrançois Tigeot plane_ptr = (uint32_t __user *)(unsigned long)plane_resp->plane_id_ptr; 23645718399fSFrançois Tigeot 23652c9916cdSFrançois Tigeot /* Plane lists are invariant, no locking needed. */ 2366a05eeebfSFrançois Tigeot drm_for_each_plane(plane, dev) { 2367ba55f2f5SFrançois Tigeot /* 2368ba55f2f5SFrançois Tigeot * Unless userspace set the 'universal planes' 2369ba55f2f5SFrançois Tigeot * capability bit, only advertise overlays. 2370ba55f2f5SFrançois Tigeot */ 2371ba55f2f5SFrançois Tigeot if (plane->type != DRM_PLANE_TYPE_OVERLAY && 2372ba55f2f5SFrançois Tigeot !file_priv->universal_planes) 2373ba55f2f5SFrançois Tigeot continue; 2374ba55f2f5SFrançois Tigeot 23752c9916cdSFrançois Tigeot if (put_user(plane->base.id, plane_ptr + copied)) 23762c9916cdSFrançois Tigeot return -EFAULT; 23775718399fSFrançois Tigeot copied++; 23785718399fSFrançois Tigeot } 23795718399fSFrançois Tigeot } 2380ba55f2f5SFrançois Tigeot plane_resp->count_planes = num_planes; 23815718399fSFrançois Tigeot 23822c9916cdSFrançois Tigeot return 0; 23835718399fSFrançois Tigeot } 23845718399fSFrançois Tigeot 23855718399fSFrançois Tigeot /** 2386ba55f2f5SFrançois Tigeot * drm_mode_getplane - get plane configuration 23875718399fSFrançois Tigeot * @dev: DRM device 23885718399fSFrançois Tigeot * @data: ioctl data 23895718399fSFrançois Tigeot * @file_priv: DRM file info 23905718399fSFrançois Tigeot * 2391ba55f2f5SFrançois Tigeot * Construct a plane configuration structure to return to the user. 2392ba55f2f5SFrançois Tigeot * 2393ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 2394ba55f2f5SFrançois Tigeot * 2395ba55f2f5SFrançois Tigeot * Returns: 23962c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 23975718399fSFrançois Tigeot */ 23985718399fSFrançois Tigeot int drm_mode_getplane(struct drm_device *dev, void *data, 23995718399fSFrançois Tigeot struct drm_file *file_priv) 24005718399fSFrançois Tigeot { 24015718399fSFrançois Tigeot struct drm_mode_get_plane *plane_resp = data; 24025718399fSFrançois Tigeot struct drm_plane *plane; 2403b5162e19SFrançois Tigeot uint32_t __user *format_ptr; 24045718399fSFrançois Tigeot 24055718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2406b5162e19SFrançois Tigeot return -EINVAL; 24075718399fSFrançois Tigeot 2408ba55f2f5SFrançois Tigeot plane = drm_plane_find(dev, plane_resp->plane_id); 24092c9916cdSFrançois Tigeot if (!plane) 24102c9916cdSFrançois Tigeot return -ENOENT; 24115718399fSFrançois Tigeot 24122c9916cdSFrançois Tigeot drm_modeset_lock(&plane->mutex, NULL); 24135718399fSFrançois Tigeot if (plane->crtc) 24145718399fSFrançois Tigeot plane_resp->crtc_id = plane->crtc->base.id; 24155718399fSFrançois Tigeot else 24165718399fSFrançois Tigeot plane_resp->crtc_id = 0; 24175718399fSFrançois Tigeot 24185718399fSFrançois Tigeot if (plane->fb) 24195718399fSFrançois Tigeot plane_resp->fb_id = plane->fb->base.id; 24205718399fSFrançois Tigeot else 24215718399fSFrançois Tigeot plane_resp->fb_id = 0; 24222c9916cdSFrançois Tigeot drm_modeset_unlock(&plane->mutex); 24235718399fSFrançois Tigeot 24245718399fSFrançois Tigeot plane_resp->plane_id = plane->base.id; 24255718399fSFrançois Tigeot plane_resp->possible_crtcs = plane->possible_crtcs; 242606fede5aSFrançois Tigeot plane_resp->gamma_size = 0; 24275718399fSFrançois Tigeot 24285718399fSFrançois Tigeot /* 24295718399fSFrançois Tigeot * This ioctl is called twice, once to determine how much space is 24305718399fSFrançois Tigeot * needed, and the 2nd time to fill it. 24315718399fSFrançois Tigeot */ 24325718399fSFrançois Tigeot if (plane->format_count && 24335718399fSFrançois Tigeot (plane_resp->count_format_types >= plane->format_count)) { 2434b5162e19SFrançois Tigeot format_ptr = (uint32_t __user *)(unsigned long)plane_resp->format_type_ptr; 2435b5162e19SFrançois Tigeot if (copy_to_user(format_ptr, 24365718399fSFrançois Tigeot plane->format_types, 24375718399fSFrançois Tigeot sizeof(uint32_t) * plane->format_count)) { 24382c9916cdSFrançois Tigeot return -EFAULT; 24395718399fSFrançois Tigeot } 24405718399fSFrançois Tigeot } 24415718399fSFrançois Tigeot plane_resp->count_format_types = plane->format_count; 24425718399fSFrançois Tigeot 24432c9916cdSFrançois Tigeot return 0; 24445718399fSFrançois Tigeot } 24455718399fSFrançois Tigeot 2446477eb7f9SFrançois Tigeot /** 2447477eb7f9SFrançois Tigeot * drm_plane_check_pixel_format - Check if the plane supports the pixel format 2448477eb7f9SFrançois Tigeot * @plane: plane to check for format support 2449477eb7f9SFrançois Tigeot * @format: the pixel format 2450477eb7f9SFrançois Tigeot * 2451477eb7f9SFrançois Tigeot * Returns: 2452477eb7f9SFrançois Tigeot * Zero of @plane has @format in its list of supported pixel formats, -EINVAL 2453477eb7f9SFrançois Tigeot * otherwise. 2454477eb7f9SFrançois Tigeot */ 2455477eb7f9SFrançois Tigeot int drm_plane_check_pixel_format(const struct drm_plane *plane, u32 format) 2456477eb7f9SFrançois Tigeot { 2457477eb7f9SFrançois Tigeot unsigned int i; 2458477eb7f9SFrançois Tigeot 2459477eb7f9SFrançois Tigeot for (i = 0; i < plane->format_count; i++) { 2460477eb7f9SFrançois Tigeot if (format == plane->format_types[i]) 2461477eb7f9SFrançois Tigeot return 0; 2462477eb7f9SFrançois Tigeot } 2463477eb7f9SFrançois Tigeot 2464477eb7f9SFrançois Tigeot return -EINVAL; 2465477eb7f9SFrançois Tigeot } 2466477eb7f9SFrançois Tigeot 2467352ff8bdSFrançois Tigeot static int check_src_coords(uint32_t src_x, uint32_t src_y, 2468352ff8bdSFrançois Tigeot uint32_t src_w, uint32_t src_h, 2469352ff8bdSFrançois Tigeot const struct drm_framebuffer *fb) 2470352ff8bdSFrançois Tigeot { 2471352ff8bdSFrançois Tigeot unsigned int fb_width, fb_height; 2472352ff8bdSFrançois Tigeot 2473352ff8bdSFrançois Tigeot fb_width = fb->width << 16; 2474352ff8bdSFrançois Tigeot fb_height = fb->height << 16; 2475352ff8bdSFrançois Tigeot 2476352ff8bdSFrançois Tigeot /* Make sure source coordinates are inside the fb. */ 2477352ff8bdSFrançois Tigeot if (src_w > fb_width || 2478352ff8bdSFrançois Tigeot src_x > fb_width - src_w || 2479352ff8bdSFrançois Tigeot src_h > fb_height || 2480352ff8bdSFrançois Tigeot src_y > fb_height - src_h) { 2481352ff8bdSFrançois Tigeot DRM_DEBUG_KMS("Invalid source coordinates " 2482352ff8bdSFrançois Tigeot "%u.%06ux%u.%06u+%u.%06u+%u.%06u\n", 2483352ff8bdSFrançois Tigeot src_w >> 16, ((src_w & 0xffff) * 15625) >> 10, 2484352ff8bdSFrançois Tigeot src_h >> 16, ((src_h & 0xffff) * 15625) >> 10, 2485352ff8bdSFrançois Tigeot src_x >> 16, ((src_x & 0xffff) * 15625) >> 10, 2486352ff8bdSFrançois Tigeot src_y >> 16, ((src_y & 0xffff) * 15625) >> 10); 2487352ff8bdSFrançois Tigeot return -ENOSPC; 2488352ff8bdSFrançois Tigeot } 2489352ff8bdSFrançois Tigeot 2490352ff8bdSFrançois Tigeot return 0; 2491352ff8bdSFrançois Tigeot } 2492352ff8bdSFrançois Tigeot 249324edb884SFrançois Tigeot /* 249424edb884SFrançois Tigeot * setplane_internal - setplane handler for internal callers 24955718399fSFrançois Tigeot * 249624edb884SFrançois Tigeot * Note that we assume an extra reference has already been taken on fb. If the 249724edb884SFrançois Tigeot * update fails, this reference will be dropped before return; if it succeeds, 249824edb884SFrançois Tigeot * the previous framebuffer (if any) will be unreferenced instead. 2499ba55f2f5SFrançois Tigeot * 250024edb884SFrançois Tigeot * src_{x,y,w,h} are provided in 16.16 fixed point format 25015718399fSFrançois Tigeot */ 25021b13d190SFrançois Tigeot static int __setplane_internal(struct drm_plane *plane, 250324edb884SFrançois Tigeot struct drm_crtc *crtc, 250424edb884SFrançois Tigeot struct drm_framebuffer *fb, 250524edb884SFrançois Tigeot int32_t crtc_x, int32_t crtc_y, 250624edb884SFrançois Tigeot uint32_t crtc_w, uint32_t crtc_h, 250724edb884SFrançois Tigeot /* src_{x,y,w,h} values are 16.16 fixed point */ 250824edb884SFrançois Tigeot uint32_t src_x, uint32_t src_y, 250924edb884SFrançois Tigeot uint32_t src_w, uint32_t src_h) 25105718399fSFrançois Tigeot { 25115718399fSFrançois Tigeot int ret = 0; 25125718399fSFrançois Tigeot 25135718399fSFrançois Tigeot /* No fb means shut it down */ 251424edb884SFrançois Tigeot if (!fb) { 25151b13d190SFrançois Tigeot plane->old_fb = plane->fb; 2516ba55f2f5SFrançois Tigeot ret = plane->funcs->disable_plane(plane); 2517ba55f2f5SFrançois Tigeot if (!ret) { 25185718399fSFrançois Tigeot plane->crtc = NULL; 25195718399fSFrançois Tigeot plane->fb = NULL; 2520ba55f2f5SFrançois Tigeot } else { 25211b13d190SFrançois Tigeot plane->old_fb = NULL; 2522ba55f2f5SFrançois Tigeot } 25235718399fSFrançois Tigeot goto out; 25245718399fSFrançois Tigeot } 25255718399fSFrançois Tigeot 252624edb884SFrançois Tigeot /* Check whether this plane is usable on this CRTC */ 252724edb884SFrançois Tigeot if (!(plane->possible_crtcs & drm_crtc_mask(crtc))) { 252824edb884SFrançois Tigeot DRM_DEBUG_KMS("Invalid crtc for plane\n"); 252924edb884SFrançois Tigeot ret = -EINVAL; 25305718399fSFrançois Tigeot goto out; 25315718399fSFrançois Tigeot } 25325718399fSFrançois Tigeot 25335718399fSFrançois Tigeot /* Check whether this plane supports the fb pixel format. */ 2534477eb7f9SFrançois Tigeot ret = drm_plane_check_pixel_format(plane, fb->pixel_format); 2535477eb7f9SFrançois Tigeot if (ret) { 253606fede5aSFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n", 253706fede5aSFrançois Tigeot drm_get_format_name(fb->pixel_format)); 25385718399fSFrançois Tigeot goto out; 25395718399fSFrançois Tigeot } 25405718399fSFrançois Tigeot 2541477eb7f9SFrançois Tigeot /* Give drivers some help against integer overflows */ 2542477eb7f9SFrançois Tigeot if (crtc_w > INT_MAX || 2543477eb7f9SFrançois Tigeot crtc_x > INT_MAX - (int32_t) crtc_w || 2544477eb7f9SFrançois Tigeot crtc_h > INT_MAX || 2545477eb7f9SFrançois Tigeot crtc_y > INT_MAX - (int32_t) crtc_h) { 2546477eb7f9SFrançois Tigeot DRM_DEBUG_KMS("Invalid CRTC coordinates %ux%u+%d+%d\n", 2547477eb7f9SFrançois Tigeot crtc_w, crtc_h, crtc_x, crtc_y); 2548352ff8bdSFrançois Tigeot ret = -ERANGE; 25495718399fSFrançois Tigeot goto out; 25505718399fSFrançois Tigeot } 25515718399fSFrançois Tigeot 2552352ff8bdSFrançois Tigeot ret = check_src_coords(src_x, src_y, src_w, src_h, fb); 2553352ff8bdSFrançois Tigeot if (ret) 2554352ff8bdSFrançois Tigeot goto out; 2555352ff8bdSFrançois Tigeot 25561b13d190SFrançois Tigeot plane->old_fb = plane->fb; 2557b5162e19SFrançois Tigeot ret = plane->funcs->update_plane(plane, crtc, fb, 255824edb884SFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 255924edb884SFrançois Tigeot src_x, src_y, src_w, src_h); 25605718399fSFrançois Tigeot if (!ret) { 25615718399fSFrançois Tigeot plane->crtc = crtc; 25625718399fSFrançois Tigeot plane->fb = fb; 25634dbb207bSFrançois Tigeot fb = NULL; 2564ba55f2f5SFrançois Tigeot } else { 25651b13d190SFrançois Tigeot plane->old_fb = NULL; 25665718399fSFrançois Tigeot } 25675718399fSFrançois Tigeot 25685718399fSFrançois Tigeot out: 25694dbb207bSFrançois Tigeot if (fb) 25704dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 25711b13d190SFrançois Tigeot if (plane->old_fb) 25721b13d190SFrançois Tigeot drm_framebuffer_unreference(plane->old_fb); 25731b13d190SFrançois Tigeot plane->old_fb = NULL; 25745718399fSFrançois Tigeot 25755718399fSFrançois Tigeot return ret; 25761b13d190SFrançois Tigeot } 257724edb884SFrançois Tigeot 25781b13d190SFrançois Tigeot static int setplane_internal(struct drm_plane *plane, 25791b13d190SFrançois Tigeot struct drm_crtc *crtc, 25801b13d190SFrançois Tigeot struct drm_framebuffer *fb, 25811b13d190SFrançois Tigeot int32_t crtc_x, int32_t crtc_y, 25821b13d190SFrançois Tigeot uint32_t crtc_w, uint32_t crtc_h, 25831b13d190SFrançois Tigeot /* src_{x,y,w,h} values are 16.16 fixed point */ 25841b13d190SFrançois Tigeot uint32_t src_x, uint32_t src_y, 25851b13d190SFrançois Tigeot uint32_t src_w, uint32_t src_h) 25861b13d190SFrançois Tigeot { 25871b13d190SFrançois Tigeot int ret; 25881b13d190SFrançois Tigeot 25891b13d190SFrançois Tigeot drm_modeset_lock_all(plane->dev); 25901b13d190SFrançois Tigeot ret = __setplane_internal(plane, crtc, fb, 25911b13d190SFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 25921b13d190SFrançois Tigeot src_x, src_y, src_w, src_h); 25931b13d190SFrançois Tigeot drm_modeset_unlock_all(plane->dev); 25941b13d190SFrançois Tigeot 25951b13d190SFrançois Tigeot return ret; 259624edb884SFrançois Tigeot } 259724edb884SFrançois Tigeot 259824edb884SFrançois Tigeot /** 259924edb884SFrançois Tigeot * drm_mode_setplane - configure a plane's configuration 260024edb884SFrançois Tigeot * @dev: DRM device 260124edb884SFrançois Tigeot * @data: ioctl data* 260224edb884SFrançois Tigeot * @file_priv: DRM file info 260324edb884SFrançois Tigeot * 260424edb884SFrançois Tigeot * Set plane configuration, including placement, fb, scaling, and other factors. 260524edb884SFrançois Tigeot * Or pass a NULL fb to disable (planes may be disabled without providing a 260624edb884SFrançois Tigeot * valid crtc). 260724edb884SFrançois Tigeot * 260824edb884SFrançois Tigeot * Returns: 26092c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 261024edb884SFrançois Tigeot */ 261124edb884SFrançois Tigeot int drm_mode_setplane(struct drm_device *dev, void *data, 261224edb884SFrançois Tigeot struct drm_file *file_priv) 261324edb884SFrançois Tigeot { 261424edb884SFrançois Tigeot struct drm_mode_set_plane *plane_req = data; 261524edb884SFrançois Tigeot struct drm_plane *plane; 261624edb884SFrançois Tigeot struct drm_crtc *crtc = NULL; 261724edb884SFrançois Tigeot struct drm_framebuffer *fb = NULL; 261824edb884SFrançois Tigeot 261924edb884SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 262024edb884SFrançois Tigeot return -EINVAL; 262124edb884SFrançois Tigeot 262224edb884SFrançois Tigeot /* 262324edb884SFrançois Tigeot * First, find the plane, crtc, and fb objects. If not available, 262424edb884SFrançois Tigeot * we don't bother to call the driver. 262524edb884SFrançois Tigeot */ 26262c9916cdSFrançois Tigeot plane = drm_plane_find(dev, plane_req->plane_id); 26272c9916cdSFrançois Tigeot if (!plane) { 262824edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown plane ID %d\n", 262924edb884SFrançois Tigeot plane_req->plane_id); 263024edb884SFrançois Tigeot return -ENOENT; 263124edb884SFrançois Tigeot } 263224edb884SFrançois Tigeot 263324edb884SFrançois Tigeot if (plane_req->fb_id) { 263424edb884SFrançois Tigeot fb = drm_framebuffer_lookup(dev, plane_req->fb_id); 263524edb884SFrançois Tigeot if (!fb) { 263624edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown framebuffer ID %d\n", 263724edb884SFrançois Tigeot plane_req->fb_id); 263824edb884SFrançois Tigeot return -ENOENT; 263924edb884SFrançois Tigeot } 264024edb884SFrançois Tigeot 26412c9916cdSFrançois Tigeot crtc = drm_crtc_find(dev, plane_req->crtc_id); 26422c9916cdSFrançois Tigeot if (!crtc) { 264324edb884SFrançois Tigeot DRM_DEBUG_KMS("Unknown crtc ID %d\n", 264424edb884SFrançois Tigeot plane_req->crtc_id); 264524edb884SFrançois Tigeot return -ENOENT; 264624edb884SFrançois Tigeot } 264724edb884SFrançois Tigeot } 264824edb884SFrançois Tigeot 264924edb884SFrançois Tigeot /* 265024edb884SFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 265124edb884SFrançois Tigeot * framebuffer depending on success. 265224edb884SFrançois Tigeot */ 265324edb884SFrançois Tigeot return setplane_internal(plane, crtc, fb, 265424edb884SFrançois Tigeot plane_req->crtc_x, plane_req->crtc_y, 265524edb884SFrançois Tigeot plane_req->crtc_w, plane_req->crtc_h, 265624edb884SFrançois Tigeot plane_req->src_x, plane_req->src_y, 265724edb884SFrançois Tigeot plane_req->src_w, plane_req->src_h); 26585718399fSFrançois Tigeot } 26595718399fSFrançois Tigeot 26605718399fSFrançois Tigeot /** 26614dbb207bSFrançois Tigeot * drm_mode_set_config_internal - helper to call ->set_config 26624dbb207bSFrançois Tigeot * @set: modeset config to set 26635718399fSFrançois Tigeot * 26644dbb207bSFrançois Tigeot * This is a little helper to wrap internal calls to the ->set_config driver 26654dbb207bSFrançois Tigeot * interface. The only thing it adds is correct refcounting dance. 2666ba55f2f5SFrançois Tigeot * 2667ba55f2f5SFrançois Tigeot * Returns: 26682c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 26694dbb207bSFrançois Tigeot */ 26704dbb207bSFrançois Tigeot int drm_mode_set_config_internal(struct drm_mode_set *set) 26714dbb207bSFrançois Tigeot { 26724dbb207bSFrançois Tigeot struct drm_crtc *crtc = set->crtc; 267306fede5aSFrançois Tigeot struct drm_framebuffer *fb; 267406fede5aSFrançois Tigeot struct drm_crtc *tmp; 26754dbb207bSFrançois Tigeot int ret; 26764dbb207bSFrançois Tigeot 267706fede5aSFrançois Tigeot /* 267806fede5aSFrançois Tigeot * NOTE: ->set_config can also disable other crtcs (if we steal all 267906fede5aSFrançois Tigeot * connectors from it), hence we need to refcount the fbs across all 268006fede5aSFrançois Tigeot * crtcs. Atomic modeset will have saner semantics ... 268106fede5aSFrançois Tigeot */ 2682a05eeebfSFrançois Tigeot drm_for_each_crtc(tmp, crtc->dev) 26831b13d190SFrançois Tigeot tmp->primary->old_fb = tmp->primary->fb; 268406fede5aSFrançois Tigeot 26854dbb207bSFrançois Tigeot fb = set->fb; 26864dbb207bSFrançois Tigeot 26874dbb207bSFrançois Tigeot ret = crtc->funcs->set_config(set); 26884dbb207bSFrançois Tigeot if (ret == 0) { 2689ba55f2f5SFrançois Tigeot crtc->primary->crtc = crtc; 2690ba55f2f5SFrançois Tigeot crtc->primary->fb = fb; 269106fede5aSFrançois Tigeot } 269206fede5aSFrançois Tigeot 2693a05eeebfSFrançois Tigeot drm_for_each_crtc(tmp, crtc->dev) { 2694ba55f2f5SFrançois Tigeot if (tmp->primary->fb) 2695ba55f2f5SFrançois Tigeot drm_framebuffer_reference(tmp->primary->fb); 26961b13d190SFrançois Tigeot if (tmp->primary->old_fb) 26971b13d190SFrançois Tigeot drm_framebuffer_unreference(tmp->primary->old_fb); 26981b13d190SFrançois Tigeot tmp->primary->old_fb = NULL; 26994dbb207bSFrançois Tigeot } 27004dbb207bSFrançois Tigeot 27014dbb207bSFrançois Tigeot return ret; 27024dbb207bSFrançois Tigeot } 27034dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_set_config_internal); 27044dbb207bSFrançois Tigeot 2705ba55f2f5SFrançois Tigeot /** 27062c9916cdSFrançois Tigeot * drm_crtc_get_hv_timing - Fetches hdisplay/vdisplay for given mode 27072c9916cdSFrançois Tigeot * @mode: mode to query 27082c9916cdSFrançois Tigeot * @hdisplay: hdisplay value to fill in 27092c9916cdSFrançois Tigeot * @vdisplay: vdisplay value to fill in 27102c9916cdSFrançois Tigeot * 27112c9916cdSFrançois Tigeot * The vdisplay value will be doubled if the specified mode is a stereo mode of 27122c9916cdSFrançois Tigeot * the appropriate layout. 27132c9916cdSFrançois Tigeot */ 27142c9916cdSFrançois Tigeot void drm_crtc_get_hv_timing(const struct drm_display_mode *mode, 27152c9916cdSFrançois Tigeot int *hdisplay, int *vdisplay) 27162c9916cdSFrançois Tigeot { 27172c9916cdSFrançois Tigeot struct drm_display_mode adjusted; 27182c9916cdSFrançois Tigeot 27192c9916cdSFrançois Tigeot drm_mode_copy(&adjusted, mode); 27202c9916cdSFrançois Tigeot drm_mode_set_crtcinfo(&adjusted, CRTC_STEREO_DOUBLE_ONLY); 27212c9916cdSFrançois Tigeot *hdisplay = adjusted.crtc_hdisplay; 27222c9916cdSFrançois Tigeot *vdisplay = adjusted.crtc_vdisplay; 27232c9916cdSFrançois Tigeot } 27242c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_crtc_get_hv_timing); 27252c9916cdSFrançois Tigeot 27262c9916cdSFrançois Tigeot /** 2727ba55f2f5SFrançois Tigeot * drm_crtc_check_viewport - Checks that a framebuffer is big enough for the 2728ba55f2f5SFrançois Tigeot * CRTC viewport 2729ba55f2f5SFrançois Tigeot * @crtc: CRTC that framebuffer will be displayed on 2730ba55f2f5SFrançois Tigeot * @x: x panning 2731ba55f2f5SFrançois Tigeot * @y: y panning 2732ba55f2f5SFrançois Tigeot * @mode: mode that framebuffer will be displayed under 2733ba55f2f5SFrançois Tigeot * @fb: framebuffer to check size of 27349edbd4a0SFrançois Tigeot */ 2735ba55f2f5SFrançois Tigeot int drm_crtc_check_viewport(const struct drm_crtc *crtc, 27369edbd4a0SFrançois Tigeot int x, int y, 27379edbd4a0SFrançois Tigeot const struct drm_display_mode *mode, 27389edbd4a0SFrançois Tigeot const struct drm_framebuffer *fb) 27399edbd4a0SFrançois Tigeot 27409edbd4a0SFrançois Tigeot { 27419edbd4a0SFrançois Tigeot int hdisplay, vdisplay; 27429edbd4a0SFrançois Tigeot 27432c9916cdSFrançois Tigeot drm_crtc_get_hv_timing(mode, &hdisplay, &vdisplay); 27449edbd4a0SFrançois Tigeot 2745352ff8bdSFrançois Tigeot if (crtc->state && 2746352ff8bdSFrançois Tigeot crtc->primary->state->rotation & (BIT(DRM_ROTATE_90) | 2747352ff8bdSFrançois Tigeot BIT(DRM_ROTATE_270))) 27489edbd4a0SFrançois Tigeot swap(hdisplay, vdisplay); 27499edbd4a0SFrançois Tigeot 2750352ff8bdSFrançois Tigeot return check_src_coords(x << 16, y << 16, 2751352ff8bdSFrançois Tigeot hdisplay << 16, vdisplay << 16, fb); 27529edbd4a0SFrançois Tigeot } 2753ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_crtc_check_viewport); 27549edbd4a0SFrançois Tigeot 27554dbb207bSFrançois Tigeot /** 27564dbb207bSFrançois Tigeot * drm_mode_setcrtc - set CRTC configuration 27574dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 27584dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 27594dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 27605718399fSFrançois Tigeot * 27615718399fSFrançois Tigeot * Build a new CRTC configuration based on user request. 27625718399fSFrançois Tigeot * 27635718399fSFrançois Tigeot * Called by the user via ioctl. 27645718399fSFrançois Tigeot * 2765ba55f2f5SFrançois Tigeot * Returns: 27662c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 27675718399fSFrançois Tigeot */ 27685718399fSFrançois Tigeot int drm_mode_setcrtc(struct drm_device *dev, void *data, 27695718399fSFrançois Tigeot struct drm_file *file_priv) 27705718399fSFrançois Tigeot { 27715718399fSFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 27725718399fSFrançois Tigeot struct drm_mode_crtc *crtc_req = data; 27735718399fSFrançois Tigeot struct drm_crtc *crtc; 27745718399fSFrançois Tigeot struct drm_connector **connector_set = NULL, *connector; 27755718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 27765718399fSFrançois Tigeot struct drm_display_mode *mode = NULL; 27775718399fSFrançois Tigeot struct drm_mode_set set; 2778b5162e19SFrançois Tigeot uint32_t __user *set_connectors_ptr; 2779b5162e19SFrançois Tigeot int ret; 27805718399fSFrançois Tigeot int i; 27815718399fSFrançois Tigeot 27825718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 2783b5162e19SFrançois Tigeot return -EINVAL; 27845718399fSFrançois Tigeot 278519c468b4SFrançois Tigeot /* 278619c468b4SFrançois Tigeot * Universal plane src offsets are only 16.16, prevent havoc for 278719c468b4SFrançois Tigeot * drivers using universal plane code internally. 278819c468b4SFrançois Tigeot */ 278919c468b4SFrançois Tigeot if (crtc_req->x & 0xffff0000 || crtc_req->y & 0xffff0000) 2790b5162e19SFrançois Tigeot return -ERANGE; 27915718399fSFrançois Tigeot 27924dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 2793ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_req->crtc_id); 2794ba55f2f5SFrançois Tigeot if (!crtc) { 27955718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", crtc_req->crtc_id); 27969edbd4a0SFrançois Tigeot ret = -ENOENT; 27975718399fSFrançois Tigeot goto out; 27985718399fSFrançois Tigeot } 2799aee94f86SFrançois Tigeot DRM_DEBUG_KMS("[CRTC:%d:%s]\n", crtc->base.id, crtc->name); 28005718399fSFrançois Tigeot 28015718399fSFrançois Tigeot if (crtc_req->mode_valid) { 28025718399fSFrançois Tigeot /* If we have a mode we need a framebuffer. */ 28035718399fSFrançois Tigeot /* If we pass -1, set the mode with the currently bound fb */ 28045718399fSFrançois Tigeot if (crtc_req->fb_id == -1) { 2805ba55f2f5SFrançois Tigeot if (!crtc->primary->fb) { 28065718399fSFrançois Tigeot DRM_DEBUG_KMS("CRTC doesn't have current FB\n"); 28075718399fSFrançois Tigeot ret = -EINVAL; 28085718399fSFrançois Tigeot goto out; 28095718399fSFrançois Tigeot } 2810ba55f2f5SFrançois Tigeot fb = crtc->primary->fb; 28114dbb207bSFrançois Tigeot /* Make refcounting symmetric with the lookup path. */ 28124dbb207bSFrançois Tigeot drm_framebuffer_reference(fb); 28135718399fSFrançois Tigeot } else { 28144dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, crtc_req->fb_id); 28154dbb207bSFrançois Tigeot if (!fb) { 28165718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown FB ID%d\n", 28175718399fSFrançois Tigeot crtc_req->fb_id); 28189edbd4a0SFrançois Tigeot ret = -ENOENT; 28195718399fSFrançois Tigeot goto out; 28205718399fSFrançois Tigeot } 28215718399fSFrançois Tigeot } 28225718399fSFrançois Tigeot 28235718399fSFrançois Tigeot mode = drm_mode_create(dev); 28245718399fSFrançois Tigeot if (!mode) { 2825b5162e19SFrançois Tigeot ret = -ENOMEM; 28265718399fSFrançois Tigeot goto out; 28275718399fSFrançois Tigeot } 28285718399fSFrançois Tigeot 282919c468b4SFrançois Tigeot ret = drm_mode_convert_umode(mode, &crtc_req->mode); 28305718399fSFrançois Tigeot if (ret) { 28315718399fSFrançois Tigeot DRM_DEBUG_KMS("Invalid mode\n"); 28325718399fSFrançois Tigeot goto out; 28335718399fSFrançois Tigeot } 28345718399fSFrançois Tigeot 2835477eb7f9SFrançois Tigeot /* 2836477eb7f9SFrançois Tigeot * Check whether the primary plane supports the fb pixel format. 2837477eb7f9SFrançois Tigeot * Drivers not implementing the universal planes API use a 2838477eb7f9SFrançois Tigeot * default formats list provided by the DRM core which doesn't 2839477eb7f9SFrançois Tigeot * match real hardware capabilities. Skip the check in that 2840477eb7f9SFrançois Tigeot * case. 2841477eb7f9SFrançois Tigeot */ 2842477eb7f9SFrançois Tigeot if (!crtc->primary->format_default) { 2843477eb7f9SFrançois Tigeot ret = drm_plane_check_pixel_format(crtc->primary, 2844477eb7f9SFrançois Tigeot fb->pixel_format); 2845477eb7f9SFrançois Tigeot if (ret) { 2846477eb7f9SFrançois Tigeot DRM_DEBUG_KMS("Invalid pixel format %s\n", 2847477eb7f9SFrançois Tigeot drm_get_format_name(fb->pixel_format)); 2848477eb7f9SFrançois Tigeot goto out; 2849477eb7f9SFrançois Tigeot } 2850477eb7f9SFrançois Tigeot } 2851477eb7f9SFrançois Tigeot 28529edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc_req->x, crtc_req->y, 28539edbd4a0SFrançois Tigeot mode, fb); 28549edbd4a0SFrançois Tigeot if (ret) 28555718399fSFrançois Tigeot goto out; 28569edbd4a0SFrançois Tigeot 28575718399fSFrançois Tigeot } 28585718399fSFrançois Tigeot 28595718399fSFrançois Tigeot if (crtc_req->count_connectors == 0 && mode) { 28605718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is 0 but mode set\n"); 2861b5162e19SFrançois Tigeot ret = -EINVAL; 28625718399fSFrançois Tigeot goto out; 28635718399fSFrançois Tigeot } 28645718399fSFrançois Tigeot 28655718399fSFrançois Tigeot if (crtc_req->count_connectors > 0 && (!mode || !fb)) { 28665718399fSFrançois Tigeot DRM_DEBUG_KMS("Count connectors is %d but no mode or fb set\n", 28675718399fSFrançois Tigeot crtc_req->count_connectors); 2868b5162e19SFrançois Tigeot ret = -EINVAL; 28695718399fSFrançois Tigeot goto out; 28705718399fSFrançois Tigeot } 28715718399fSFrançois Tigeot 28725718399fSFrançois Tigeot if (crtc_req->count_connectors > 0) { 28735718399fSFrançois Tigeot u32 out_id; 28745718399fSFrançois Tigeot 28755718399fSFrançois Tigeot /* Avoid unbounded kernel memory allocation */ 28765718399fSFrançois Tigeot if (crtc_req->count_connectors > config->num_connector) { 2877b5162e19SFrançois Tigeot ret = -EINVAL; 28785718399fSFrançois Tigeot goto out; 28795718399fSFrançois Tigeot } 28805718399fSFrançois Tigeot 28815718399fSFrançois Tigeot connector_set = kmalloc(crtc_req->count_connectors * 28824dbb207bSFrançois Tigeot sizeof(struct drm_connector *), 28834dbb207bSFrançois Tigeot M_DRM, M_WAITOK); 2884b5162e19SFrançois Tigeot if (!connector_set) { 2885b5162e19SFrançois Tigeot ret = -ENOMEM; 2886b5162e19SFrançois Tigeot goto out; 2887b5162e19SFrançois Tigeot } 28885718399fSFrançois Tigeot 28895718399fSFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) { 28908621f407SFrançois Tigeot connector_set[i] = NULL; 2891b5162e19SFrançois Tigeot set_connectors_ptr = (uint32_t __user *)(unsigned long)crtc_req->set_connectors_ptr; 2892b5162e19SFrançois Tigeot if (get_user(out_id, &set_connectors_ptr[i])) { 2893b5162e19SFrançois Tigeot ret = -EFAULT; 28945718399fSFrançois Tigeot goto out; 28955718399fSFrançois Tigeot } 28965718399fSFrançois Tigeot 28978621f407SFrançois Tigeot connector = drm_connector_lookup(dev, out_id); 2898ba55f2f5SFrançois Tigeot if (!connector) { 28995718399fSFrançois Tigeot DRM_DEBUG_KMS("Connector id %d unknown\n", 29005718399fSFrançois Tigeot out_id); 29019edbd4a0SFrançois Tigeot ret = -ENOENT; 29025718399fSFrançois Tigeot goto out; 29035718399fSFrançois Tigeot } 29045718399fSFrançois Tigeot DRM_DEBUG_KMS("[CONNECTOR:%d:%s]\n", 29055718399fSFrançois Tigeot connector->base.id, 2906ba55f2f5SFrançois Tigeot connector->name); 29075718399fSFrançois Tigeot 29085718399fSFrançois Tigeot connector_set[i] = connector; 29095718399fSFrançois Tigeot } 29105718399fSFrançois Tigeot } 29115718399fSFrançois Tigeot 29125718399fSFrançois Tigeot set.crtc = crtc; 29135718399fSFrançois Tigeot set.x = crtc_req->x; 29145718399fSFrançois Tigeot set.y = crtc_req->y; 29155718399fSFrançois Tigeot set.mode = mode; 29165718399fSFrançois Tigeot set.connectors = connector_set; 29175718399fSFrançois Tigeot set.num_connectors = crtc_req->count_connectors; 29185718399fSFrançois Tigeot set.fb = fb; 29194dbb207bSFrançois Tigeot ret = drm_mode_set_config_internal(&set); 29205718399fSFrançois Tigeot 29215718399fSFrançois Tigeot out: 29224dbb207bSFrançois Tigeot if (fb) 29234dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 29244dbb207bSFrançois Tigeot 29258621f407SFrançois Tigeot if (connector_set) { 29268621f407SFrançois Tigeot for (i = 0; i < crtc_req->count_connectors; i++) { 29278621f407SFrançois Tigeot if (connector_set[i]) 29288621f407SFrançois Tigeot drm_connector_unreference(connector_set[i]); 29298621f407SFrançois Tigeot } 29308621f407SFrançois Tigeot } 29314dbb207bSFrançois Tigeot kfree(connector_set); 29325718399fSFrançois Tigeot drm_mode_destroy(dev, mode); 29334dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 29345718399fSFrançois Tigeot return ret; 29355718399fSFrançois Tigeot } 29365718399fSFrançois Tigeot 293724edb884SFrançois Tigeot /** 293824edb884SFrançois Tigeot * drm_mode_cursor_universal - translate legacy cursor ioctl call into a 293924edb884SFrançois Tigeot * universal plane handler call 294024edb884SFrançois Tigeot * @crtc: crtc to update cursor for 294124edb884SFrançois Tigeot * @req: data pointer for the ioctl 294224edb884SFrançois Tigeot * @file_priv: drm file for the ioctl call 294324edb884SFrançois Tigeot * 294424edb884SFrançois Tigeot * Legacy cursor ioctl's work directly with driver buffer handles. To 294524edb884SFrançois Tigeot * translate legacy ioctl calls into universal plane handler calls, we need to 294624edb884SFrançois Tigeot * wrap the native buffer handle in a drm_framebuffer. 294724edb884SFrançois Tigeot * 294824edb884SFrançois Tigeot * Note that we assume any handle passed to the legacy ioctls was a 32-bit ARGB 294924edb884SFrançois Tigeot * buffer with a pitch of 4*width; the universal plane interface should be used 295024edb884SFrançois Tigeot * directly in cases where the hardware can support other buffer settings and 295124edb884SFrançois Tigeot * userspace wants to make use of these capabilities. 295224edb884SFrançois Tigeot * 295324edb884SFrançois Tigeot * Returns: 29542c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 295524edb884SFrançois Tigeot */ 295624edb884SFrançois Tigeot static int drm_mode_cursor_universal(struct drm_crtc *crtc, 295724edb884SFrançois Tigeot struct drm_mode_cursor2 *req, 295824edb884SFrançois Tigeot struct drm_file *file_priv) 295924edb884SFrançois Tigeot { 296024edb884SFrançois Tigeot struct drm_device *dev = crtc->dev; 296124edb884SFrançois Tigeot struct drm_framebuffer *fb = NULL; 296224edb884SFrançois Tigeot struct drm_mode_fb_cmd2 fbreq = { 296324edb884SFrançois Tigeot .width = req->width, 296424edb884SFrançois Tigeot .height = req->height, 296524edb884SFrançois Tigeot .pixel_format = DRM_FORMAT_ARGB8888, 296624edb884SFrançois Tigeot .pitches = { req->width * 4 }, 296724edb884SFrançois Tigeot .handles = { req->handle }, 296824edb884SFrançois Tigeot }; 296924edb884SFrançois Tigeot int32_t crtc_x, crtc_y; 297024edb884SFrançois Tigeot uint32_t crtc_w = 0, crtc_h = 0; 297124edb884SFrançois Tigeot uint32_t src_w = 0, src_h = 0; 297224edb884SFrançois Tigeot int ret = 0; 297324edb884SFrançois Tigeot 297424edb884SFrançois Tigeot BUG_ON(!crtc->cursor); 29751b13d190SFrançois Tigeot WARN_ON(crtc->cursor->crtc != crtc && crtc->cursor->crtc != NULL); 297624edb884SFrançois Tigeot 297724edb884SFrançois Tigeot /* 297824edb884SFrançois Tigeot * Obtain fb we'll be using (either new or existing) and take an extra 297924edb884SFrançois Tigeot * reference to it if fb != null. setplane will take care of dropping 298024edb884SFrançois Tigeot * the reference if the plane update fails. 298124edb884SFrançois Tigeot */ 298224edb884SFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 298324edb884SFrançois Tigeot if (req->handle) { 29842c9916cdSFrançois Tigeot fb = internal_framebuffer_create(dev, &fbreq, file_priv); 298524edb884SFrançois Tigeot if (IS_ERR(fb)) { 298624edb884SFrançois Tigeot DRM_DEBUG_KMS("failed to wrap cursor buffer in drm framebuffer\n"); 298724edb884SFrançois Tigeot return PTR_ERR(fb); 298824edb884SFrançois Tigeot } 298924edb884SFrançois Tigeot } else { 299024edb884SFrançois Tigeot fb = NULL; 299124edb884SFrançois Tigeot } 299224edb884SFrançois Tigeot } else { 299324edb884SFrançois Tigeot fb = crtc->cursor->fb; 299424edb884SFrançois Tigeot if (fb) 299524edb884SFrançois Tigeot drm_framebuffer_reference(fb); 299624edb884SFrançois Tigeot } 299724edb884SFrançois Tigeot 299824edb884SFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 299924edb884SFrançois Tigeot crtc_x = req->x; 300024edb884SFrançois Tigeot crtc_y = req->y; 300124edb884SFrançois Tigeot } else { 300224edb884SFrançois Tigeot crtc_x = crtc->cursor_x; 300324edb884SFrançois Tigeot crtc_y = crtc->cursor_y; 300424edb884SFrançois Tigeot } 300524edb884SFrançois Tigeot 300624edb884SFrançois Tigeot if (fb) { 300724edb884SFrançois Tigeot crtc_w = fb->width; 300824edb884SFrançois Tigeot crtc_h = fb->height; 300924edb884SFrançois Tigeot src_w = fb->width << 16; 301024edb884SFrançois Tigeot src_h = fb->height << 16; 301124edb884SFrançois Tigeot } 301224edb884SFrançois Tigeot 301324edb884SFrançois Tigeot /* 301424edb884SFrançois Tigeot * setplane_internal will take care of deref'ing either the old or new 301524edb884SFrançois Tigeot * framebuffer depending on success. 301624edb884SFrançois Tigeot */ 30171b13d190SFrançois Tigeot ret = __setplane_internal(crtc->cursor, crtc, fb, 301824edb884SFrançois Tigeot crtc_x, crtc_y, crtc_w, crtc_h, 301924edb884SFrançois Tigeot 0, 0, src_w, src_h); 302024edb884SFrançois Tigeot 302124edb884SFrançois Tigeot /* Update successful; save new cursor position, if necessary */ 302224edb884SFrançois Tigeot if (ret == 0 && req->flags & DRM_MODE_CURSOR_MOVE) { 302324edb884SFrançois Tigeot crtc->cursor_x = req->x; 302424edb884SFrançois Tigeot crtc->cursor_y = req->y; 302524edb884SFrançois Tigeot } 302624edb884SFrançois Tigeot 302724edb884SFrançois Tigeot return ret; 302824edb884SFrançois Tigeot } 302924edb884SFrançois Tigeot 303006fede5aSFrançois Tigeot static int drm_mode_cursor_common(struct drm_device *dev, 303106fede5aSFrançois Tigeot struct drm_mode_cursor2 *req, 303206fede5aSFrançois Tigeot struct drm_file *file_priv) 30335718399fSFrançois Tigeot { 30345718399fSFrançois Tigeot struct drm_crtc *crtc; 30355718399fSFrançois Tigeot int ret = 0; 30365718399fSFrançois Tigeot 30375718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3038b5162e19SFrançois Tigeot return -EINVAL; 30395718399fSFrançois Tigeot 3040b5162e19SFrançois Tigeot if (!req->flags || (~DRM_MODE_CURSOR_FLAGS & req->flags)) 3041b5162e19SFrançois Tigeot return -EINVAL; 30425718399fSFrançois Tigeot 3043ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, req->crtc_id); 3044ba55f2f5SFrançois Tigeot if (!crtc) { 30455718399fSFrançois Tigeot DRM_DEBUG_KMS("Unknown CRTC ID %d\n", req->crtc_id); 30469edbd4a0SFrançois Tigeot return -ENOENT; 30475718399fSFrançois Tigeot } 30485718399fSFrançois Tigeot 304924edb884SFrançois Tigeot /* 305024edb884SFrançois Tigeot * If this crtc has a universal cursor plane, call that plane's update 305124edb884SFrançois Tigeot * handler rather than using legacy cursor handlers. 305224edb884SFrançois Tigeot */ 30532c9916cdSFrançois Tigeot drm_modeset_lock_crtc(crtc, crtc->cursor); 30541b13d190SFrançois Tigeot if (crtc->cursor) { 30551b13d190SFrançois Tigeot ret = drm_mode_cursor_universal(crtc, req, file_priv); 30561b13d190SFrançois Tigeot goto out; 30571b13d190SFrançois Tigeot } 305824edb884SFrançois Tigeot 30595718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_BO) { 306006fede5aSFrançois Tigeot if (!crtc->funcs->cursor_set && !crtc->funcs->cursor_set2) { 3061b5162e19SFrançois Tigeot ret = -ENXIO; 30625718399fSFrançois Tigeot goto out; 30635718399fSFrançois Tigeot } 30645718399fSFrançois Tigeot /* Turns off the cursor if handle is 0 */ 306506fede5aSFrançois Tigeot if (crtc->funcs->cursor_set2) 306606fede5aSFrançois Tigeot ret = crtc->funcs->cursor_set2(crtc, file_priv, req->handle, 306706fede5aSFrançois Tigeot req->width, req->height, req->hot_x, req->hot_y); 306806fede5aSFrançois Tigeot else 3069b5162e19SFrançois Tigeot ret = crtc->funcs->cursor_set(crtc, file_priv, req->handle, 30705718399fSFrançois Tigeot req->width, req->height); 30715718399fSFrançois Tigeot } 30725718399fSFrançois Tigeot 30735718399fSFrançois Tigeot if (req->flags & DRM_MODE_CURSOR_MOVE) { 30745718399fSFrançois Tigeot if (crtc->funcs->cursor_move) { 30755718399fSFrançois Tigeot ret = crtc->funcs->cursor_move(crtc, req->x, req->y); 30765718399fSFrançois Tigeot } else { 3077b5162e19SFrançois Tigeot ret = -EFAULT; 30785718399fSFrançois Tigeot goto out; 30795718399fSFrançois Tigeot } 30805718399fSFrançois Tigeot } 30815718399fSFrançois Tigeot out: 30821b13d190SFrançois Tigeot drm_modeset_unlock_crtc(crtc); 30834dbb207bSFrançois Tigeot 30845718399fSFrançois Tigeot return ret; 308506fede5aSFrançois Tigeot 308606fede5aSFrançois Tigeot } 3087ba55f2f5SFrançois Tigeot 3088ba55f2f5SFrançois Tigeot 3089ba55f2f5SFrançois Tigeot /** 3090ba55f2f5SFrançois Tigeot * drm_mode_cursor_ioctl - set CRTC's cursor configuration 3091ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 3092ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 3093ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 3094ba55f2f5SFrançois Tigeot * 3095ba55f2f5SFrançois Tigeot * Set the cursor configuration based on user request. 3096ba55f2f5SFrançois Tigeot * 3097ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3098ba55f2f5SFrançois Tigeot * 3099ba55f2f5SFrançois Tigeot * Returns: 31002c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 3101ba55f2f5SFrançois Tigeot */ 310206fede5aSFrançois Tigeot int drm_mode_cursor_ioctl(struct drm_device *dev, 310306fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 310406fede5aSFrançois Tigeot { 310506fede5aSFrançois Tigeot struct drm_mode_cursor *req = data; 310606fede5aSFrançois Tigeot struct drm_mode_cursor2 new_req; 310706fede5aSFrançois Tigeot 310806fede5aSFrançois Tigeot memcpy(&new_req, req, sizeof(struct drm_mode_cursor)); 310906fede5aSFrançois Tigeot new_req.hot_x = new_req.hot_y = 0; 311006fede5aSFrançois Tigeot 311106fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, &new_req, file_priv); 311206fede5aSFrançois Tigeot } 311306fede5aSFrançois Tigeot 3114ba55f2f5SFrançois Tigeot /** 3115ba55f2f5SFrançois Tigeot * drm_mode_cursor2_ioctl - set CRTC's cursor configuration 3116ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 3117ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 3118ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 3119ba55f2f5SFrançois Tigeot * 3120ba55f2f5SFrançois Tigeot * Set the cursor configuration based on user request. This implements the 2nd 3121ba55f2f5SFrançois Tigeot * version of the cursor ioctl, which allows userspace to additionally specify 3122ba55f2f5SFrançois Tigeot * the hotspot of the pointer. 3123ba55f2f5SFrançois Tigeot * 3124ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3125ba55f2f5SFrançois Tigeot * 3126ba55f2f5SFrançois Tigeot * Returns: 31272c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 3128ba55f2f5SFrançois Tigeot */ 312906fede5aSFrançois Tigeot int drm_mode_cursor2_ioctl(struct drm_device *dev, 313006fede5aSFrançois Tigeot void *data, struct drm_file *file_priv) 313106fede5aSFrançois Tigeot { 313206fede5aSFrançois Tigeot struct drm_mode_cursor2 *req = data; 31332c9916cdSFrançois Tigeot 313406fede5aSFrançois Tigeot return drm_mode_cursor_common(dev, req, file_priv); 31355718399fSFrançois Tigeot } 31365718399fSFrançois Tigeot 3137ba55f2f5SFrançois Tigeot /** 3138ba55f2f5SFrançois Tigeot * drm_mode_legacy_fb_format - compute drm fourcc code from legacy description 3139ba55f2f5SFrançois Tigeot * @bpp: bits per pixels 3140ba55f2f5SFrançois Tigeot * @depth: bit depth per pixel 3141ba55f2f5SFrançois Tigeot * 3142ba55f2f5SFrançois Tigeot * Computes a drm fourcc pixel format code for the given @bpp/@depth values. 3143ba55f2f5SFrançois Tigeot * Useful in fbdev emulation code, since that deals in those values. 3144ba55f2f5SFrançois Tigeot */ 31455718399fSFrançois Tigeot uint32_t drm_mode_legacy_fb_format(uint32_t bpp, uint32_t depth) 31465718399fSFrançois Tigeot { 31475718399fSFrançois Tigeot uint32_t fmt; 31485718399fSFrançois Tigeot 31495718399fSFrançois Tigeot switch (bpp) { 31505718399fSFrançois Tigeot case 8: 31514dbb207bSFrançois Tigeot fmt = DRM_FORMAT_C8; 31525718399fSFrançois Tigeot break; 31535718399fSFrançois Tigeot case 16: 31545718399fSFrançois Tigeot if (depth == 15) 31555718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB1555; 31565718399fSFrançois Tigeot else 31575718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB565; 31585718399fSFrançois Tigeot break; 31595718399fSFrançois Tigeot case 24: 31605718399fSFrançois Tigeot fmt = DRM_FORMAT_RGB888; 31615718399fSFrançois Tigeot break; 31625718399fSFrançois Tigeot case 32: 31635718399fSFrançois Tigeot if (depth == 24) 31645718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 31655718399fSFrançois Tigeot else if (depth == 30) 31665718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB2101010; 31675718399fSFrançois Tigeot else 31685718399fSFrançois Tigeot fmt = DRM_FORMAT_ARGB8888; 31695718399fSFrançois Tigeot break; 31705718399fSFrançois Tigeot default: 3171b5162e19SFrançois Tigeot DRM_ERROR("bad bpp, assuming x8r8g8b8 pixel format\n"); 31725718399fSFrançois Tigeot fmt = DRM_FORMAT_XRGB8888; 31735718399fSFrançois Tigeot break; 31745718399fSFrançois Tigeot } 31755718399fSFrançois Tigeot 31765718399fSFrançois Tigeot return fmt; 31775718399fSFrançois Tigeot } 3178b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_legacy_fb_format); 31795718399fSFrançois Tigeot 31805718399fSFrançois Tigeot /** 31815718399fSFrançois Tigeot * drm_mode_addfb - add an FB to the graphics configuration 31824dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 31834dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 31844dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 31855718399fSFrançois Tigeot * 3186ba55f2f5SFrançois Tigeot * Add a new FB to the specified CRTC, given a user request. This is the 31872c9916cdSFrançois Tigeot * original addfb ioctl which only supported RGB formats. 31885718399fSFrançois Tigeot * 31895718399fSFrançois Tigeot * Called by the user via ioctl. 31905718399fSFrançois Tigeot * 3191ba55f2f5SFrançois Tigeot * Returns: 31922c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 31935718399fSFrançois Tigeot */ 31945718399fSFrançois Tigeot int drm_mode_addfb(struct drm_device *dev, 31955718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 31965718399fSFrançois Tigeot { 31975718399fSFrançois Tigeot struct drm_mode_fb_cmd *or = data; 31985718399fSFrançois Tigeot struct drm_mode_fb_cmd2 r = {}; 31992c9916cdSFrançois Tigeot int ret; 32005718399fSFrançois Tigeot 32012c9916cdSFrançois Tigeot /* convert to new format and call new ioctl */ 32025718399fSFrançois Tigeot r.fb_id = or->fb_id; 32035718399fSFrançois Tigeot r.width = or->width; 32045718399fSFrançois Tigeot r.height = or->height; 32055718399fSFrançois Tigeot r.pitches[0] = or->pitch; 32065718399fSFrançois Tigeot r.pixel_format = drm_mode_legacy_fb_format(or->bpp, or->depth); 32075718399fSFrançois Tigeot r.handles[0] = or->handle; 32085718399fSFrançois Tigeot 32092c9916cdSFrançois Tigeot ret = drm_mode_addfb2(dev, &r, file_priv); 32102c9916cdSFrançois Tigeot if (ret) 32112c9916cdSFrançois Tigeot return ret; 32125718399fSFrançois Tigeot 32132c9916cdSFrançois Tigeot or->fb_id = r.fb_id; 32145718399fSFrançois Tigeot 3215917ec290SFrançois Tigeot return 0; 32165718399fSFrançois Tigeot } 32175718399fSFrançois Tigeot 3218b5162e19SFrançois Tigeot static int format_check(const struct drm_mode_fb_cmd2 *r) 32195718399fSFrançois Tigeot { 32205718399fSFrançois Tigeot uint32_t format = r->pixel_format & ~DRM_FORMAT_BIG_ENDIAN; 32215718399fSFrançois Tigeot 32225718399fSFrançois Tigeot switch (format) { 32235718399fSFrançois Tigeot case DRM_FORMAT_C8: 32245718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 32255718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 32265718399fSFrançois Tigeot case DRM_FORMAT_XRGB4444: 32275718399fSFrançois Tigeot case DRM_FORMAT_XBGR4444: 32285718399fSFrançois Tigeot case DRM_FORMAT_RGBX4444: 32295718399fSFrançois Tigeot case DRM_FORMAT_BGRX4444: 32305718399fSFrançois Tigeot case DRM_FORMAT_ARGB4444: 32315718399fSFrançois Tigeot case DRM_FORMAT_ABGR4444: 32325718399fSFrançois Tigeot case DRM_FORMAT_RGBA4444: 32335718399fSFrançois Tigeot case DRM_FORMAT_BGRA4444: 32345718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 32355718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 32365718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 32375718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 32385718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 32395718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 32405718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 32415718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 32425718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 32435718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 32445718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 32455718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 32465718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 32475718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 32485718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 32495718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 32505718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 32515718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 32525718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 32535718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 32545718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 32555718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 32565718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 32575718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 32585718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 32595718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 32605718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 32615718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 32625718399fSFrançois Tigeot case DRM_FORMAT_YUYV: 32635718399fSFrançois Tigeot case DRM_FORMAT_YVYU: 32645718399fSFrançois Tigeot case DRM_FORMAT_UYVY: 32655718399fSFrançois Tigeot case DRM_FORMAT_VYUY: 32665718399fSFrançois Tigeot case DRM_FORMAT_AYUV: 32675718399fSFrançois Tigeot case DRM_FORMAT_NV12: 32685718399fSFrançois Tigeot case DRM_FORMAT_NV21: 32695718399fSFrançois Tigeot case DRM_FORMAT_NV16: 32705718399fSFrançois Tigeot case DRM_FORMAT_NV61: 3271b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 3272b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 32735718399fSFrançois Tigeot case DRM_FORMAT_YUV410: 32745718399fSFrançois Tigeot case DRM_FORMAT_YVU410: 32755718399fSFrançois Tigeot case DRM_FORMAT_YUV411: 32765718399fSFrançois Tigeot case DRM_FORMAT_YVU411: 32775718399fSFrançois Tigeot case DRM_FORMAT_YUV420: 32785718399fSFrançois Tigeot case DRM_FORMAT_YVU420: 32795718399fSFrançois Tigeot case DRM_FORMAT_YUV422: 32805718399fSFrançois Tigeot case DRM_FORMAT_YVU422: 32815718399fSFrançois Tigeot case DRM_FORMAT_YUV444: 32825718399fSFrançois Tigeot case DRM_FORMAT_YVU444: 32835718399fSFrançois Tigeot return 0; 32845718399fSFrançois Tigeot default: 32859edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("invalid pixel format %s\n", 32869edbd4a0SFrançois Tigeot drm_get_format_name(r->pixel_format)); 3287b5162e19SFrançois Tigeot return -EINVAL; 32885718399fSFrançois Tigeot } 32895718399fSFrançois Tigeot } 32905718399fSFrançois Tigeot 3291b5162e19SFrançois Tigeot static int framebuffer_check(const struct drm_mode_fb_cmd2 *r) 3292b5162e19SFrançois Tigeot { 3293b5162e19SFrançois Tigeot int ret, hsub, vsub, num_planes, i; 3294b5162e19SFrançois Tigeot 3295b5162e19SFrançois Tigeot ret = format_check(r); 3296b5162e19SFrançois Tigeot if (ret) { 329706fede5aSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer format %s\n", 329806fede5aSFrançois Tigeot drm_get_format_name(r->pixel_format)); 3299b5162e19SFrançois Tigeot return ret; 3300b5162e19SFrançois Tigeot } 3301b5162e19SFrançois Tigeot 3302b5162e19SFrançois Tigeot hsub = drm_format_horz_chroma_subsampling(r->pixel_format); 3303b5162e19SFrançois Tigeot vsub = drm_format_vert_chroma_subsampling(r->pixel_format); 3304b5162e19SFrançois Tigeot num_planes = drm_format_num_planes(r->pixel_format); 3305b5162e19SFrançois Tigeot 3306b5162e19SFrançois Tigeot if (r->width == 0 || r->width % hsub) { 33072c9916cdSFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %u\n", r->width); 3308b5162e19SFrançois Tigeot return -EINVAL; 3309b5162e19SFrançois Tigeot } 3310b5162e19SFrançois Tigeot 3311b5162e19SFrançois Tigeot if (r->height == 0 || r->height % vsub) { 3312b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %u\n", r->height); 3313b5162e19SFrançois Tigeot return -EINVAL; 3314b5162e19SFrançois Tigeot } 3315b5162e19SFrançois Tigeot 3316b5162e19SFrançois Tigeot for (i = 0; i < num_planes; i++) { 3317b5162e19SFrançois Tigeot unsigned int width = r->width / (i != 0 ? hsub : 1); 3318b5162e19SFrançois Tigeot unsigned int height = r->height / (i != 0 ? vsub : 1); 3319b5162e19SFrançois Tigeot unsigned int cpp = drm_format_plane_cpp(r->pixel_format, i); 3320b5162e19SFrançois Tigeot 3321b5162e19SFrançois Tigeot if (!r->handles[i]) { 3322b5162e19SFrançois Tigeot DRM_DEBUG_KMS("no buffer object handle for plane %d\n", i); 3323b5162e19SFrançois Tigeot return -EINVAL; 3324b5162e19SFrançois Tigeot } 3325b5162e19SFrançois Tigeot 3326b5162e19SFrançois Tigeot if ((uint64_t) width * cpp > UINT_MAX) 3327b5162e19SFrançois Tigeot return -ERANGE; 3328b5162e19SFrançois Tigeot 3329b5162e19SFrançois Tigeot if ((uint64_t) height * r->pitches[i] + r->offsets[i] > UINT_MAX) 3330b5162e19SFrançois Tigeot return -ERANGE; 3331b5162e19SFrançois Tigeot 3332b5162e19SFrançois Tigeot if (r->pitches[i] < width * cpp) { 3333b5162e19SFrançois Tigeot DRM_DEBUG_KMS("bad pitch %u for plane %d\n", r->pitches[i], i); 3334b5162e19SFrançois Tigeot return -EINVAL; 3335b5162e19SFrançois Tigeot } 3336477eb7f9SFrançois Tigeot 3337477eb7f9SFrançois Tigeot if (r->modifier[i] && !(r->flags & DRM_MODE_FB_MODIFIERS)) { 3338f77dbd6cSFrançois Tigeot DRM_DEBUG_KMS("bad fb modifier %llu for plane %d\n", 3339477eb7f9SFrançois Tigeot r->modifier[i], i); 3340477eb7f9SFrançois Tigeot return -EINVAL; 3341477eb7f9SFrançois Tigeot } 334219c468b4SFrançois Tigeot 334319c468b4SFrançois Tigeot /* modifier specific checks: */ 334419c468b4SFrançois Tigeot switch (r->modifier[i]) { 334519c468b4SFrançois Tigeot case DRM_FORMAT_MOD_SAMSUNG_64_32_TILE: 334619c468b4SFrançois Tigeot /* NOTE: the pitch restriction may be lifted later if it turns 334719c468b4SFrançois Tigeot * out that no hw has this restriction: 334819c468b4SFrançois Tigeot */ 334919c468b4SFrançois Tigeot if (r->pixel_format != DRM_FORMAT_NV12 || 335019c468b4SFrançois Tigeot width % 128 || height % 32 || 335119c468b4SFrançois Tigeot r->pitches[i] % 128) { 335219c468b4SFrançois Tigeot DRM_DEBUG_KMS("bad modifier data for plane %d\n", i); 335319c468b4SFrançois Tigeot return -EINVAL; 335419c468b4SFrançois Tigeot } 335519c468b4SFrançois Tigeot break; 335619c468b4SFrançois Tigeot 335719c468b4SFrançois Tigeot default: 335819c468b4SFrançois Tigeot break; 335919c468b4SFrançois Tigeot } 336019c468b4SFrançois Tigeot } 336119c468b4SFrançois Tigeot 336219c468b4SFrançois Tigeot for (i = num_planes; i < 4; i++) { 336319c468b4SFrançois Tigeot if (r->modifier[i]) { 336419c468b4SFrançois Tigeot DRM_DEBUG_KMS("non-zero modifier for unused plane %d\n", i); 336519c468b4SFrançois Tigeot return -EINVAL; 336619c468b4SFrançois Tigeot } 336719c468b4SFrançois Tigeot 336819c468b4SFrançois Tigeot /* Pre-FB_MODIFIERS userspace didn't clear the structs properly. */ 336919c468b4SFrançois Tigeot if (!(r->flags & DRM_MODE_FB_MODIFIERS)) 337019c468b4SFrançois Tigeot continue; 337119c468b4SFrançois Tigeot 337219c468b4SFrançois Tigeot if (r->handles[i]) { 337319c468b4SFrançois Tigeot DRM_DEBUG_KMS("buffer object handle for unused plane %d\n", i); 337419c468b4SFrançois Tigeot return -EINVAL; 337519c468b4SFrançois Tigeot } 337619c468b4SFrançois Tigeot 337719c468b4SFrançois Tigeot if (r->pitches[i]) { 337819c468b4SFrançois Tigeot DRM_DEBUG_KMS("non-zero pitch for unused plane %d\n", i); 337919c468b4SFrançois Tigeot return -EINVAL; 338019c468b4SFrançois Tigeot } 338119c468b4SFrançois Tigeot 338219c468b4SFrançois Tigeot if (r->offsets[i]) { 338319c468b4SFrançois Tigeot DRM_DEBUG_KMS("non-zero offset for unused plane %d\n", i); 338419c468b4SFrançois Tigeot return -EINVAL; 338519c468b4SFrançois Tigeot } 3386b5162e19SFrançois Tigeot } 3387b5162e19SFrançois Tigeot 3388b5162e19SFrançois Tigeot return 0; 3389b5162e19SFrançois Tigeot } 3390b5162e19SFrançois Tigeot 33912c9916cdSFrançois Tigeot static struct drm_framebuffer * 33922c9916cdSFrançois Tigeot internal_framebuffer_create(struct drm_device *dev, 3393aee94f86SFrançois Tigeot const struct drm_mode_fb_cmd2 *r, 339424edb884SFrançois Tigeot struct drm_file *file_priv) 339524edb884SFrançois Tigeot { 339624edb884SFrançois Tigeot struct drm_mode_config *config = &dev->mode_config; 339724edb884SFrançois Tigeot struct drm_framebuffer *fb; 339824edb884SFrançois Tigeot int ret; 339924edb884SFrançois Tigeot 3400477eb7f9SFrançois Tigeot if (r->flags & ~(DRM_MODE_FB_INTERLACED | DRM_MODE_FB_MODIFIERS)) { 340124edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer flags 0x%08x\n", r->flags); 340224edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 340324edb884SFrançois Tigeot } 340424edb884SFrançois Tigeot 340524edb884SFrançois Tigeot if ((config->min_width > r->width) || (r->width > config->max_width)) { 340624edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer width %d, should be >= %d && <= %d\n", 340724edb884SFrançois Tigeot r->width, config->min_width, config->max_width); 340824edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 340924edb884SFrançois Tigeot } 341024edb884SFrançois Tigeot if ((config->min_height > r->height) || (r->height > config->max_height)) { 341124edb884SFrançois Tigeot DRM_DEBUG_KMS("bad framebuffer height %d, should be >= %d && <= %d\n", 341224edb884SFrançois Tigeot r->height, config->min_height, config->max_height); 341324edb884SFrançois Tigeot return ERR_PTR(-EINVAL); 341424edb884SFrançois Tigeot } 341524edb884SFrançois Tigeot 3416477eb7f9SFrançois Tigeot if (r->flags & DRM_MODE_FB_MODIFIERS && 3417477eb7f9SFrançois Tigeot !dev->mode_config.allow_fb_modifiers) { 3418477eb7f9SFrançois Tigeot DRM_DEBUG_KMS("driver does not support fb modifiers\n"); 3419477eb7f9SFrançois Tigeot return ERR_PTR(-EINVAL); 3420477eb7f9SFrançois Tigeot } 3421477eb7f9SFrançois Tigeot 342224edb884SFrançois Tigeot ret = framebuffer_check(r); 342324edb884SFrançois Tigeot if (ret) 342424edb884SFrançois Tigeot return ERR_PTR(ret); 342524edb884SFrançois Tigeot 342624edb884SFrançois Tigeot fb = dev->mode_config.funcs->fb_create(dev, file_priv, r); 342724edb884SFrançois Tigeot if (IS_ERR(fb)) { 342824edb884SFrançois Tigeot DRM_DEBUG_KMS("could not create framebuffer\n"); 342924edb884SFrançois Tigeot return fb; 343024edb884SFrançois Tigeot } 343124edb884SFrançois Tigeot 343224edb884SFrançois Tigeot return fb; 343324edb884SFrançois Tigeot } 343424edb884SFrançois Tigeot 34355718399fSFrançois Tigeot /** 34365718399fSFrançois Tigeot * drm_mode_addfb2 - add an FB to the graphics configuration 34374dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 34384dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 34394dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 34405718399fSFrançois Tigeot * 3441ba55f2f5SFrançois Tigeot * Add a new FB to the specified CRTC, given a user request with format. This is 3442ba55f2f5SFrançois Tigeot * the 2nd version of the addfb ioctl, which supports multi-planar framebuffers 3443ba55f2f5SFrançois Tigeot * and uses fourcc codes as pixel format specifiers. 34445718399fSFrançois Tigeot * 34455718399fSFrançois Tigeot * Called by the user via ioctl. 34465718399fSFrançois Tigeot * 3447ba55f2f5SFrançois Tigeot * Returns: 34482c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 34495718399fSFrançois Tigeot */ 34505718399fSFrançois Tigeot int drm_mode_addfb2(struct drm_device *dev, 34515718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 34525718399fSFrançois Tigeot { 34532c9916cdSFrançois Tigeot struct drm_mode_fb_cmd2 *r = data; 34545718399fSFrançois Tigeot struct drm_framebuffer *fb; 34555718399fSFrançois Tigeot 34565718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3457b5162e19SFrançois Tigeot return -EINVAL; 3458b5162e19SFrançois Tigeot 34592c9916cdSFrançois Tigeot fb = internal_framebuffer_create(dev, r, file_priv); 346024edb884SFrançois Tigeot if (IS_ERR(fb)) 34614dbb207bSFrançois Tigeot return PTR_ERR(fb); 34625718399fSFrançois Tigeot 34632c9916cdSFrançois Tigeot DRM_DEBUG_KMS("[FB:%d]\n", fb->base.id); 34642c9916cdSFrançois Tigeot r->fb_id = fb->base.id; 34658621f407SFrançois Tigeot 34668621f407SFrançois Tigeot /* Transfer ownership to the filp for reaping on close */ 34678621f407SFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 34682c9916cdSFrançois Tigeot list_add(&fb->filp_head, &file_priv->fbs); 34692c9916cdSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 34702c9916cdSFrançois Tigeot 347124edb884SFrançois Tigeot return 0; 34725718399fSFrançois Tigeot } 34735718399fSFrançois Tigeot 34748621f407SFrançois Tigeot struct drm_mode_rmfb_work { 34758621f407SFrançois Tigeot struct work_struct work; 34768621f407SFrançois Tigeot struct list_head fbs; 34778621f407SFrançois Tigeot }; 34788621f407SFrançois Tigeot 34798621f407SFrançois Tigeot static void drm_mode_rmfb_work_fn(struct work_struct *w) 34808621f407SFrançois Tigeot { 34818621f407SFrançois Tigeot struct drm_mode_rmfb_work *arg = container_of(w, typeof(*arg), work); 34828621f407SFrançois Tigeot 34838621f407SFrançois Tigeot while (!list_empty(&arg->fbs)) { 34848621f407SFrançois Tigeot struct drm_framebuffer *fb = 34858621f407SFrançois Tigeot list_first_entry(&arg->fbs, typeof(*fb), filp_head); 34868621f407SFrançois Tigeot 34878621f407SFrançois Tigeot list_del_init(&fb->filp_head); 34888621f407SFrançois Tigeot drm_framebuffer_remove(fb); 34898621f407SFrançois Tigeot } 34908621f407SFrançois Tigeot } 34918621f407SFrançois Tigeot 34925718399fSFrançois Tigeot /** 34935718399fSFrançois Tigeot * drm_mode_rmfb - remove an FB from the configuration 34944dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 34954dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 34964dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 34975718399fSFrançois Tigeot * 34985718399fSFrançois Tigeot * Remove the FB specified by the user. 34995718399fSFrançois Tigeot * 35005718399fSFrançois Tigeot * Called by the user via ioctl. 35015718399fSFrançois Tigeot * 3502ba55f2f5SFrançois Tigeot * Returns: 35032c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 35045718399fSFrançois Tigeot */ 35055718399fSFrançois Tigeot int drm_mode_rmfb(struct drm_device *dev, 35065718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 35075718399fSFrançois Tigeot { 35085718399fSFrançois Tigeot struct drm_framebuffer *fb = NULL; 35095718399fSFrançois Tigeot struct drm_framebuffer *fbl = NULL; 35105718399fSFrançois Tigeot uint32_t *id = data; 35115718399fSFrançois Tigeot int found = 0; 35125718399fSFrançois Tigeot 35135718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3514b5162e19SFrançois Tigeot return -EINVAL; 35155718399fSFrançois Tigeot 35168621f407SFrançois Tigeot fb = drm_framebuffer_lookup(dev, *id); 35174dbb207bSFrançois Tigeot if (!fb) 35188621f407SFrançois Tigeot return -ENOENT; 35195718399fSFrançois Tigeot 35208621f407SFrançois Tigeot mutex_lock(&file_priv->fbs_lock); 35215718399fSFrançois Tigeot list_for_each_entry(fbl, &file_priv->fbs, filp_head) 35225718399fSFrançois Tigeot if (fb == fbl) 35235718399fSFrançois Tigeot found = 1; 35248621f407SFrançois Tigeot if (!found) { 35258621f407SFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 35268621f407SFrançois Tigeot goto fail_unref; 35278621f407SFrançois Tigeot } 35285718399fSFrançois Tigeot 35294dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 35304dbb207bSFrançois Tigeot mutex_unlock(&file_priv->fbs_lock); 35315718399fSFrançois Tigeot 35328621f407SFrançois Tigeot /* drop the reference we picked up in framebuffer lookup */ 35338621f407SFrançois Tigeot drm_framebuffer_unreference(fb); 35348621f407SFrançois Tigeot 35358621f407SFrançois Tigeot /* 35368621f407SFrançois Tigeot * we now own the reference that was stored in the fbs list 35378621f407SFrançois Tigeot * 35388621f407SFrançois Tigeot * drm_framebuffer_remove may fail with -EINTR on pending signals, 35398621f407SFrançois Tigeot * so run this in a separate stack as there's no way to correctly 35408621f407SFrançois Tigeot * handle this after the fb is already removed from the lookup table. 35418621f407SFrançois Tigeot */ 35428621f407SFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 35438621f407SFrançois Tigeot struct drm_mode_rmfb_work arg; 35448621f407SFrançois Tigeot 35458621f407SFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 35468621f407SFrançois Tigeot INIT_LIST_HEAD(&arg.fbs); 35478621f407SFrançois Tigeot list_add_tail(&fb->filp_head, &arg.fbs); 35488621f407SFrançois Tigeot 35498621f407SFrançois Tigeot schedule_work(&arg.work); 35508621f407SFrançois Tigeot flush_work(&arg.work); 35518621f407SFrançois Tigeot destroy_work_on_stack(&arg.work); 35528621f407SFrançois Tigeot } else 3553352ff8bdSFrançois Tigeot drm_framebuffer_unreference(fb); 35545718399fSFrançois Tigeot 35554dbb207bSFrançois Tigeot return 0; 35564dbb207bSFrançois Tigeot 35578621f407SFrançois Tigeot fail_unref: 35588621f407SFrançois Tigeot drm_framebuffer_unreference(fb); 35599edbd4a0SFrançois Tigeot return -ENOENT; 35605718399fSFrançois Tigeot } 35615718399fSFrançois Tigeot 35625718399fSFrançois Tigeot /** 35635718399fSFrançois Tigeot * drm_mode_getfb - get FB info 35644dbb207bSFrançois Tigeot * @dev: drm device for the ioctl 35654dbb207bSFrançois Tigeot * @data: data pointer for the ioctl 35664dbb207bSFrançois Tigeot * @file_priv: drm file for the ioctl call 35675718399fSFrançois Tigeot * 35685718399fSFrançois Tigeot * Lookup the FB given its ID and return info about it. 35695718399fSFrançois Tigeot * 35705718399fSFrançois Tigeot * Called by the user via ioctl. 35715718399fSFrançois Tigeot * 3572ba55f2f5SFrançois Tigeot * Returns: 35732c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 35745718399fSFrançois Tigeot */ 35755718399fSFrançois Tigeot int drm_mode_getfb(struct drm_device *dev, 35765718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 35775718399fSFrançois Tigeot { 35785718399fSFrançois Tigeot struct drm_mode_fb_cmd *r = data; 35795718399fSFrançois Tigeot struct drm_framebuffer *fb; 35804dbb207bSFrançois Tigeot int ret; 35815718399fSFrançois Tigeot 35825718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3583b5162e19SFrançois Tigeot return -EINVAL; 35845718399fSFrançois Tigeot 35854dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 35864dbb207bSFrançois Tigeot if (!fb) 35879edbd4a0SFrançois Tigeot return -ENOENT; 35885718399fSFrançois Tigeot 35895718399fSFrançois Tigeot r->height = fb->height; 35905718399fSFrançois Tigeot r->width = fb->width; 35915718399fSFrançois Tigeot r->depth = fb->depth; 35925718399fSFrançois Tigeot r->bpp = fb->bits_per_pixel; 35935718399fSFrançois Tigeot r->pitch = fb->pitches[0]; 3594ba55f2f5SFrançois Tigeot if (fb->funcs->create_handle) { 35954dbb207bSFrançois Tigeot ret = fb->funcs->create_handle(fb, file_priv, &r->handle); 3596ba55f2f5SFrançois Tigeot } else { 35974dbb207bSFrançois Tigeot ret = -ENODEV; 3598ba55f2f5SFrançois Tigeot } 35995718399fSFrançois Tigeot 36004dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 36014dbb207bSFrançois Tigeot 36025718399fSFrançois Tigeot return ret; 36035718399fSFrançois Tigeot } 36045718399fSFrançois Tigeot 3605ba55f2f5SFrançois Tigeot /** 3606ba55f2f5SFrançois Tigeot * drm_mode_dirtyfb_ioctl - flush frontbuffer rendering on an FB 3607ba55f2f5SFrançois Tigeot * @dev: drm device for the ioctl 3608ba55f2f5SFrançois Tigeot * @data: data pointer for the ioctl 3609ba55f2f5SFrançois Tigeot * @file_priv: drm file for the ioctl call 3610ba55f2f5SFrançois Tigeot * 3611ba55f2f5SFrançois Tigeot * Lookup the FB and flush out the damaged area supplied by userspace as a clip 3612ba55f2f5SFrançois Tigeot * rectangle list. Generic userspace which does frontbuffer rendering must call 3613ba55f2f5SFrançois Tigeot * this ioctl to flush out the changes on manual-update display outputs, e.g. 3614ba55f2f5SFrançois Tigeot * usb display-link, mipi manual update panels or edp panel self refresh modes. 3615ba55f2f5SFrançois Tigeot * 3616ba55f2f5SFrançois Tigeot * Modesetting drivers which always update the frontbuffer do not need to 3617ba55f2f5SFrançois Tigeot * implement the corresponding ->dirty framebuffer callback. 3618ba55f2f5SFrançois Tigeot * 3619ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 3620ba55f2f5SFrançois Tigeot * 3621ba55f2f5SFrançois Tigeot * Returns: 36222c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 3623ba55f2f5SFrançois Tigeot */ 36245718399fSFrançois Tigeot int drm_mode_dirtyfb_ioctl(struct drm_device *dev, 36255718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 36265718399fSFrançois Tigeot { 36275718399fSFrançois Tigeot struct drm_clip_rect __user *clips_ptr; 36285718399fSFrançois Tigeot struct drm_clip_rect *clips = NULL; 36295718399fSFrançois Tigeot struct drm_mode_fb_dirty_cmd *r = data; 36305718399fSFrançois Tigeot struct drm_framebuffer *fb; 36315718399fSFrançois Tigeot unsigned flags; 36325718399fSFrançois Tigeot int num_clips; 3633b5162e19SFrançois Tigeot int ret; 36345718399fSFrançois Tigeot 36355718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 3636b5162e19SFrançois Tigeot return -EINVAL; 36375718399fSFrançois Tigeot 36384dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, r->fb_id); 36394dbb207bSFrançois Tigeot if (!fb) 36409edbd4a0SFrançois Tigeot return -ENOENT; 36415718399fSFrançois Tigeot 36425718399fSFrançois Tigeot num_clips = r->num_clips; 3643b5162e19SFrançois Tigeot clips_ptr = (struct drm_clip_rect __user *)(unsigned long)r->clips_ptr; 36445718399fSFrançois Tigeot 36455718399fSFrançois Tigeot if (!num_clips != !clips_ptr) { 3646b5162e19SFrançois Tigeot ret = -EINVAL; 36475718399fSFrançois Tigeot goto out_err1; 36485718399fSFrançois Tigeot } 36495718399fSFrançois Tigeot 36505718399fSFrançois Tigeot flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags; 36515718399fSFrançois Tigeot 36525718399fSFrançois Tigeot /* If userspace annotates copy, clips must come in pairs */ 36535718399fSFrançois Tigeot if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) { 3654b5162e19SFrançois Tigeot ret = -EINVAL; 36555718399fSFrançois Tigeot goto out_err1; 36565718399fSFrançois Tigeot } 36575718399fSFrançois Tigeot 36585718399fSFrançois Tigeot if (num_clips && clips_ptr) { 36595718399fSFrançois Tigeot if (num_clips < 0 || num_clips > DRM_MODE_FB_DIRTY_MAX_CLIPS) { 3660b5162e19SFrançois Tigeot ret = -EINVAL; 36615718399fSFrançois Tigeot goto out_err1; 36625718399fSFrançois Tigeot } 36632c9916cdSFrançois Tigeot clips = kcalloc(num_clips, sizeof(*clips), GFP_KERNEL); 3664b5162e19SFrançois Tigeot if (!clips) { 3665b5162e19SFrançois Tigeot ret = -ENOMEM; 3666b5162e19SFrançois Tigeot goto out_err1; 3667b5162e19SFrançois Tigeot } 36685718399fSFrançois Tigeot 3669b5162e19SFrançois Tigeot ret = copy_from_user(clips, clips_ptr, 3670b5162e19SFrançois Tigeot num_clips * sizeof(*clips)); 3671b5162e19SFrançois Tigeot if (ret) { 3672b5162e19SFrançois Tigeot ret = -EFAULT; 36735718399fSFrançois Tigeot goto out_err2; 36745718399fSFrançois Tigeot } 3675b5162e19SFrançois Tigeot } 36765718399fSFrançois Tigeot 36775718399fSFrançois Tigeot if (fb->funcs->dirty) { 3678b5162e19SFrançois Tigeot ret = fb->funcs->dirty(fb, file_priv, flags, r->color, 36795718399fSFrançois Tigeot clips, num_clips); 36805718399fSFrançois Tigeot } else { 3681b5162e19SFrançois Tigeot ret = -ENOSYS; 36825718399fSFrançois Tigeot } 36835718399fSFrançois Tigeot 36845718399fSFrançois Tigeot out_err2: 36854dbb207bSFrançois Tigeot kfree(clips); 36865718399fSFrançois Tigeot out_err1: 36874dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 36884dbb207bSFrançois Tigeot 36895718399fSFrançois Tigeot return ret; 36905718399fSFrançois Tigeot } 36915718399fSFrançois Tigeot 36925718399fSFrançois Tigeot /** 36935718399fSFrançois Tigeot * drm_fb_release - remove and free the FBs on this file 36944dbb207bSFrançois Tigeot * @priv: drm file for the ioctl 36955718399fSFrançois Tigeot * 36965718399fSFrançois Tigeot * Destroy all the FBs associated with @filp. 36975718399fSFrançois Tigeot * 36985718399fSFrançois Tigeot * Called by the user via ioctl. 36995718399fSFrançois Tigeot * 3700ba55f2f5SFrançois Tigeot * Returns: 37012c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 37025718399fSFrançois Tigeot */ 37035718399fSFrançois Tigeot void drm_fb_release(struct drm_file *priv) 37045718399fSFrançois Tigeot { 37055718399fSFrançois Tigeot struct drm_framebuffer *fb, *tfb; 37068621f407SFrançois Tigeot struct drm_mode_rmfb_work arg; 37078621f407SFrançois Tigeot 37088621f407SFrançois Tigeot INIT_LIST_HEAD(&arg.fbs); 37095718399fSFrançois Tigeot 37101b13d190SFrançois Tigeot /* 37111b13d190SFrançois Tigeot * When the file gets released that means no one else can access the fb 37122c9916cdSFrançois Tigeot * list any more, so no need to grab fpriv->fbs_lock. And we need to 37131b13d190SFrançois Tigeot * avoid upsetting lockdep since the universal cursor code adds a 37141b13d190SFrançois Tigeot * framebuffer while holding mutex locks. 37151b13d190SFrançois Tigeot * 37161b13d190SFrançois Tigeot * Note that a real deadlock between fpriv->fbs_lock and the modeset 37171b13d190SFrançois Tigeot * locks is impossible here since no one else but this function can get 37181b13d190SFrançois Tigeot * at it any more. 37191b13d190SFrançois Tigeot */ 37205718399fSFrançois Tigeot list_for_each_entry_safe(fb, tfb, &priv->fbs, filp_head) { 37218621f407SFrançois Tigeot if (drm_framebuffer_read_refcount(fb) > 1) { 37228621f407SFrançois Tigeot list_move_tail(&fb->filp_head, &arg.fbs); 37238621f407SFrançois Tigeot } else { 37244dbb207bSFrançois Tigeot list_del_init(&fb->filp_head); 37254dbb207bSFrançois Tigeot 3726352ff8bdSFrançois Tigeot /* This drops the fpriv->fbs reference. */ 3727352ff8bdSFrançois Tigeot drm_framebuffer_unreference(fb); 37285718399fSFrançois Tigeot } 37295718399fSFrançois Tigeot } 37305718399fSFrançois Tigeot 37318621f407SFrançois Tigeot if (!list_empty(&arg.fbs)) { 37328621f407SFrançois Tigeot INIT_WORK_ONSTACK(&arg.work, drm_mode_rmfb_work_fn); 37338621f407SFrançois Tigeot 37348621f407SFrançois Tigeot schedule_work(&arg.work); 37358621f407SFrançois Tigeot flush_work(&arg.work); 37368621f407SFrançois Tigeot destroy_work_on_stack(&arg.work); 37378621f407SFrançois Tigeot } 37388621f407SFrançois Tigeot } 37398621f407SFrançois Tigeot 3740ba55f2f5SFrançois Tigeot /** 3741ba55f2f5SFrançois Tigeot * drm_property_create - create a new property type 3742ba55f2f5SFrançois Tigeot * @dev: drm device 3743ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3744ba55f2f5SFrançois Tigeot * @name: name of the property 3745ba55f2f5SFrançois Tigeot * @num_values: number of pre-defined values 3746ba55f2f5SFrançois Tigeot * 3747ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3748ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3749ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3750ba55f2f5SFrançois Tigeot * 37512c9916cdSFrançois Tigeot * Note that the DRM core keeps a per-device list of properties and that, if 37522c9916cdSFrançois Tigeot * drm_mode_config_cleanup() is called, it will destroy all properties created 37532c9916cdSFrançois Tigeot * by the driver. 37542c9916cdSFrançois Tigeot * 3755ba55f2f5SFrançois Tigeot * Returns: 3756ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3757ba55f2f5SFrançois Tigeot */ 37585718399fSFrançois Tigeot struct drm_property *drm_property_create(struct drm_device *dev, int flags, 37595718399fSFrançois Tigeot const char *name, int num_values) 37605718399fSFrançois Tigeot { 37615718399fSFrançois Tigeot struct drm_property *property = NULL; 37625718399fSFrançois Tigeot int ret; 37635718399fSFrançois Tigeot 37644dbb207bSFrançois Tigeot property = kzalloc(sizeof(struct drm_property), GFP_KERNEL); 3765b5162e19SFrançois Tigeot if (!property) 3766b5162e19SFrançois Tigeot return NULL; 37675718399fSFrançois Tigeot 3768ba55f2f5SFrançois Tigeot property->dev = dev; 3769ba55f2f5SFrançois Tigeot 37705718399fSFrançois Tigeot if (num_values) { 37712c9916cdSFrançois Tigeot property->values = kcalloc(num_values, sizeof(uint64_t), 37722c9916cdSFrançois Tigeot GFP_KERNEL); 3773b5162e19SFrançois Tigeot if (!property->values) 3774b5162e19SFrançois Tigeot goto fail; 37755718399fSFrançois Tigeot } 37765718399fSFrançois Tigeot 37775718399fSFrançois Tigeot ret = drm_mode_object_get(dev, &property->base, DRM_MODE_OBJECT_PROPERTY); 37785718399fSFrançois Tigeot if (ret) 37795718399fSFrançois Tigeot goto fail; 3780b5162e19SFrançois Tigeot 37815718399fSFrançois Tigeot property->flags = flags; 37825718399fSFrançois Tigeot property->num_values = num_values; 37832c9916cdSFrançois Tigeot INIT_LIST_HEAD(&property->enum_list); 37845718399fSFrançois Tigeot 37855718399fSFrançois Tigeot if (name) { 37865718399fSFrançois Tigeot strncpy(property->name, name, DRM_PROP_NAME_LEN); 37875718399fSFrançois Tigeot property->name[DRM_PROP_NAME_LEN-1] = '\0'; 37885718399fSFrançois Tigeot } 37895718399fSFrançois Tigeot 37905718399fSFrançois Tigeot list_add_tail(&property->head, &dev->mode_config.property_list); 3791ba55f2f5SFrançois Tigeot 3792ba55f2f5SFrançois Tigeot WARN_ON(!drm_property_type_valid(property)); 3793ba55f2f5SFrançois Tigeot 37945718399fSFrançois Tigeot return property; 37955718399fSFrançois Tigeot fail: 37964dbb207bSFrançois Tigeot kfree(property->values); 37974dbb207bSFrançois Tigeot kfree(property); 3798b5162e19SFrançois Tigeot return NULL; 37995718399fSFrançois Tigeot } 3800b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create); 38015718399fSFrançois Tigeot 3802ba55f2f5SFrançois Tigeot /** 380324edb884SFrançois Tigeot * drm_property_create_enum - create a new enumeration property type 3804ba55f2f5SFrançois Tigeot * @dev: drm device 3805ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3806ba55f2f5SFrançois Tigeot * @name: name of the property 3807ba55f2f5SFrançois Tigeot * @props: enumeration lists with property values 3808ba55f2f5SFrançois Tigeot * @num_values: number of pre-defined values 3809ba55f2f5SFrançois Tigeot * 3810ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3811ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3812ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3813ba55f2f5SFrançois Tigeot * 3814ba55f2f5SFrançois Tigeot * Userspace is only allowed to set one of the predefined values for enumeration 3815ba55f2f5SFrançois Tigeot * properties. 3816ba55f2f5SFrançois Tigeot * 3817ba55f2f5SFrançois Tigeot * Returns: 3818ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3819ba55f2f5SFrançois Tigeot */ 38205718399fSFrançois Tigeot struct drm_property *drm_property_create_enum(struct drm_device *dev, int flags, 38215718399fSFrançois Tigeot const char *name, 38225718399fSFrançois Tigeot const struct drm_prop_enum_list *props, 38235718399fSFrançois Tigeot int num_values) 38245718399fSFrançois Tigeot { 38255718399fSFrançois Tigeot struct drm_property *property; 38265718399fSFrançois Tigeot int i, ret; 38275718399fSFrançois Tigeot 38285718399fSFrançois Tigeot flags |= DRM_MODE_PROP_ENUM; 38295718399fSFrançois Tigeot 38305718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 38315718399fSFrançois Tigeot if (!property) 38325718399fSFrançois Tigeot return NULL; 38335718399fSFrançois Tigeot 38345718399fSFrançois Tigeot for (i = 0; i < num_values; i++) { 38355718399fSFrançois Tigeot ret = drm_property_add_enum(property, i, 38365718399fSFrançois Tigeot props[i].type, 38375718399fSFrançois Tigeot props[i].name); 38385718399fSFrançois Tigeot if (ret) { 38395718399fSFrançois Tigeot drm_property_destroy(dev, property); 38405718399fSFrançois Tigeot return NULL; 38415718399fSFrançois Tigeot } 38425718399fSFrançois Tigeot } 38435718399fSFrançois Tigeot 38445718399fSFrançois Tigeot return property; 38455718399fSFrançois Tigeot } 3846b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_enum); 3847b5162e19SFrançois Tigeot 3848ba55f2f5SFrançois Tigeot /** 384924edb884SFrançois Tigeot * drm_property_create_bitmask - create a new bitmask property type 3850ba55f2f5SFrançois Tigeot * @dev: drm device 3851ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3852ba55f2f5SFrançois Tigeot * @name: name of the property 3853ba55f2f5SFrançois Tigeot * @props: enumeration lists with property bitflags 38541b13d190SFrançois Tigeot * @num_props: size of the @props array 38551b13d190SFrançois Tigeot * @supported_bits: bitmask of all supported enumeration values 3856ba55f2f5SFrançois Tigeot * 38571b13d190SFrançois Tigeot * This creates a new bitmask drm property which can then be attached to a drm 3858ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3859ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3860ba55f2f5SFrançois Tigeot * 3861ba55f2f5SFrançois Tigeot * Compared to plain enumeration properties userspace is allowed to set any 3862ba55f2f5SFrançois Tigeot * or'ed together combination of the predefined property bitflag values 3863ba55f2f5SFrançois Tigeot * 3864ba55f2f5SFrançois Tigeot * Returns: 3865ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3866ba55f2f5SFrançois Tigeot */ 3867b5162e19SFrançois Tigeot struct drm_property *drm_property_create_bitmask(struct drm_device *dev, 3868b5162e19SFrançois Tigeot int flags, const char *name, 3869b5162e19SFrançois Tigeot const struct drm_prop_enum_list *props, 38701b13d190SFrançois Tigeot int num_props, 38711b13d190SFrançois Tigeot uint64_t supported_bits) 3872b5162e19SFrançois Tigeot { 3873b5162e19SFrançois Tigeot struct drm_property *property; 38741b13d190SFrançois Tigeot int i, ret, index = 0; 38751b13d190SFrançois Tigeot int num_values = hweight64(supported_bits); 3876b5162e19SFrançois Tigeot 3877b5162e19SFrançois Tigeot flags |= DRM_MODE_PROP_BITMASK; 3878b5162e19SFrançois Tigeot 3879b5162e19SFrançois Tigeot property = drm_property_create(dev, flags, name, num_values); 3880b5162e19SFrançois Tigeot if (!property) 3881b5162e19SFrançois Tigeot return NULL; 38821b13d190SFrançois Tigeot for (i = 0; i < num_props; i++) { 38831b13d190SFrançois Tigeot if (!(supported_bits & (1ULL << props[i].type))) 38841b13d190SFrançois Tigeot continue; 3885b5162e19SFrançois Tigeot 38861b13d190SFrançois Tigeot if (WARN_ON(index >= num_values)) { 38871b13d190SFrançois Tigeot drm_property_destroy(dev, property); 38881b13d190SFrançois Tigeot return NULL; 38891b13d190SFrançois Tigeot } 38901b13d190SFrançois Tigeot 38911b13d190SFrançois Tigeot ret = drm_property_add_enum(property, index++, 3892b5162e19SFrançois Tigeot props[i].type, 3893b5162e19SFrançois Tigeot props[i].name); 3894b5162e19SFrançois Tigeot if (ret) { 3895b5162e19SFrançois Tigeot drm_property_destroy(dev, property); 3896b5162e19SFrançois Tigeot return NULL; 3897b5162e19SFrançois Tigeot } 3898b5162e19SFrançois Tigeot } 3899b5162e19SFrançois Tigeot 3900b5162e19SFrançois Tigeot return property; 3901b5162e19SFrançois Tigeot } 3902b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bitmask); 39035718399fSFrançois Tigeot 3904ba55f2f5SFrançois Tigeot static struct drm_property *property_create_range(struct drm_device *dev, 3905ba55f2f5SFrançois Tigeot int flags, const char *name, 39065718399fSFrançois Tigeot uint64_t min, uint64_t max) 39075718399fSFrançois Tigeot { 39085718399fSFrançois Tigeot struct drm_property *property; 39095718399fSFrançois Tigeot 39105718399fSFrançois Tigeot property = drm_property_create(dev, flags, name, 2); 39115718399fSFrançois Tigeot if (!property) 39125718399fSFrançois Tigeot return NULL; 39135718399fSFrançois Tigeot 39145718399fSFrançois Tigeot property->values[0] = min; 39155718399fSFrançois Tigeot property->values[1] = max; 39165718399fSFrançois Tigeot 39175718399fSFrançois Tigeot return property; 39185718399fSFrançois Tigeot } 3919ba55f2f5SFrançois Tigeot 3920ba55f2f5SFrançois Tigeot /** 39212c9916cdSFrançois Tigeot * drm_property_create_range - create a new unsigned ranged property type 3922ba55f2f5SFrançois Tigeot * @dev: drm device 3923ba55f2f5SFrançois Tigeot * @flags: flags specifying the property type 3924ba55f2f5SFrançois Tigeot * @name: name of the property 3925ba55f2f5SFrançois Tigeot * @min: minimum value of the property 3926ba55f2f5SFrançois Tigeot * @max: maximum value of the property 3927ba55f2f5SFrançois Tigeot * 3928ba55f2f5SFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 3929ba55f2f5SFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 3930ba55f2f5SFrançois Tigeot * freed with drm_property_destroy. 3931ba55f2f5SFrançois Tigeot * 39322c9916cdSFrançois Tigeot * Userspace is allowed to set any unsigned integer value in the (min, max) 39332c9916cdSFrançois Tigeot * range inclusive. 3934ba55f2f5SFrançois Tigeot * 3935ba55f2f5SFrançois Tigeot * Returns: 3936ba55f2f5SFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 3937ba55f2f5SFrançois Tigeot */ 3938ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_range(struct drm_device *dev, int flags, 3939ba55f2f5SFrançois Tigeot const char *name, 3940ba55f2f5SFrançois Tigeot uint64_t min, uint64_t max) 3941ba55f2f5SFrançois Tigeot { 3942ba55f2f5SFrançois Tigeot return property_create_range(dev, DRM_MODE_PROP_RANGE | flags, 3943ba55f2f5SFrançois Tigeot name, min, max); 3944ba55f2f5SFrançois Tigeot } 3945b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_range); 39465718399fSFrançois Tigeot 39472c9916cdSFrançois Tigeot /** 39482c9916cdSFrançois Tigeot * drm_property_create_signed_range - create a new signed ranged property type 39492c9916cdSFrançois Tigeot * @dev: drm device 39502c9916cdSFrançois Tigeot * @flags: flags specifying the property type 39512c9916cdSFrançois Tigeot * @name: name of the property 39522c9916cdSFrançois Tigeot * @min: minimum value of the property 39532c9916cdSFrançois Tigeot * @max: maximum value of the property 39542c9916cdSFrançois Tigeot * 39552c9916cdSFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 39562c9916cdSFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 39572c9916cdSFrançois Tigeot * freed with drm_property_destroy. 39582c9916cdSFrançois Tigeot * 39592c9916cdSFrançois Tigeot * Userspace is allowed to set any signed integer value in the (min, max) 39602c9916cdSFrançois Tigeot * range inclusive. 39612c9916cdSFrançois Tigeot * 39622c9916cdSFrançois Tigeot * Returns: 39632c9916cdSFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 39642c9916cdSFrançois Tigeot */ 3965ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_signed_range(struct drm_device *dev, 3966ba55f2f5SFrançois Tigeot int flags, const char *name, 3967ba55f2f5SFrançois Tigeot int64_t min, int64_t max) 3968ba55f2f5SFrançois Tigeot { 3969ba55f2f5SFrançois Tigeot return property_create_range(dev, DRM_MODE_PROP_SIGNED_RANGE | flags, 3970ba55f2f5SFrançois Tigeot name, I642U64(min), I642U64(max)); 3971ba55f2f5SFrançois Tigeot } 3972ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_signed_range); 3973ba55f2f5SFrançois Tigeot 39742c9916cdSFrançois Tigeot /** 39752c9916cdSFrançois Tigeot * drm_property_create_object - create a new object property type 39762c9916cdSFrançois Tigeot * @dev: drm device 39772c9916cdSFrançois Tigeot * @flags: flags specifying the property type 39782c9916cdSFrançois Tigeot * @name: name of the property 39792c9916cdSFrançois Tigeot * @type: object type from DRM_MODE_OBJECT_* defines 39802c9916cdSFrançois Tigeot * 39812c9916cdSFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 39822c9916cdSFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 39832c9916cdSFrançois Tigeot * freed with drm_property_destroy. 39842c9916cdSFrançois Tigeot * 39852c9916cdSFrançois Tigeot * Userspace is only allowed to set this to any property value of the given 39862c9916cdSFrançois Tigeot * @type. Only useful for atomic properties, which is enforced. 39872c9916cdSFrançois Tigeot * 39882c9916cdSFrançois Tigeot * Returns: 39892c9916cdSFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 39902c9916cdSFrançois Tigeot */ 3991ba55f2f5SFrançois Tigeot struct drm_property *drm_property_create_object(struct drm_device *dev, 3992ba55f2f5SFrançois Tigeot int flags, const char *name, uint32_t type) 3993ba55f2f5SFrançois Tigeot { 3994ba55f2f5SFrançois Tigeot struct drm_property *property; 3995ba55f2f5SFrançois Tigeot 3996ba55f2f5SFrançois Tigeot flags |= DRM_MODE_PROP_OBJECT; 3997ba55f2f5SFrançois Tigeot 39982c9916cdSFrançois Tigeot if (WARN_ON(!(flags & DRM_MODE_PROP_ATOMIC))) 39992c9916cdSFrançois Tigeot return NULL; 40002c9916cdSFrançois Tigeot 4001ba55f2f5SFrançois Tigeot property = drm_property_create(dev, flags, name, 1); 4002ba55f2f5SFrançois Tigeot if (!property) 4003ba55f2f5SFrançois Tigeot return NULL; 4004ba55f2f5SFrançois Tigeot 4005ba55f2f5SFrançois Tigeot property->values[0] = type; 4006ba55f2f5SFrançois Tigeot 4007ba55f2f5SFrançois Tigeot return property; 4008ba55f2f5SFrançois Tigeot } 4009ba55f2f5SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_object); 4010ba55f2f5SFrançois Tigeot 4011ba55f2f5SFrançois Tigeot /** 40122c9916cdSFrançois Tigeot * drm_property_create_bool - create a new boolean property type 40132c9916cdSFrançois Tigeot * @dev: drm device 40142c9916cdSFrançois Tigeot * @flags: flags specifying the property type 40152c9916cdSFrançois Tigeot * @name: name of the property 40162c9916cdSFrançois Tigeot * 40172c9916cdSFrançois Tigeot * This creates a new generic drm property which can then be attached to a drm 40182c9916cdSFrançois Tigeot * object with drm_object_attach_property. The returned property object must be 40192c9916cdSFrançois Tigeot * freed with drm_property_destroy. 40202c9916cdSFrançois Tigeot * 40212c9916cdSFrançois Tigeot * This is implemented as a ranged property with only {0, 1} as valid values. 40222c9916cdSFrançois Tigeot * 40232c9916cdSFrançois Tigeot * Returns: 40242c9916cdSFrançois Tigeot * A pointer to the newly created property on success, NULL on failure. 40252c9916cdSFrançois Tigeot */ 40262c9916cdSFrançois Tigeot struct drm_property *drm_property_create_bool(struct drm_device *dev, int flags, 40272c9916cdSFrançois Tigeot const char *name) 40282c9916cdSFrançois Tigeot { 40292c9916cdSFrançois Tigeot return drm_property_create_range(dev, flags, name, 0, 1); 40302c9916cdSFrançois Tigeot } 40312c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_property_create_bool); 40322c9916cdSFrançois Tigeot 40332c9916cdSFrançois Tigeot /** 4034ba55f2f5SFrançois Tigeot * drm_property_add_enum - add a possible value to an enumeration property 4035ba55f2f5SFrançois Tigeot * @property: enumeration property to change 4036ba55f2f5SFrançois Tigeot * @index: index of the new enumeration 4037ba55f2f5SFrançois Tigeot * @value: value of the new enumeration 4038ba55f2f5SFrançois Tigeot * @name: symbolic name of the new enumeration 4039ba55f2f5SFrançois Tigeot * 4040ba55f2f5SFrançois Tigeot * This functions adds enumerations to a property. 4041ba55f2f5SFrançois Tigeot * 4042ba55f2f5SFrançois Tigeot * It's use is deprecated, drivers should use one of the more specific helpers 4043ba55f2f5SFrançois Tigeot * to directly create the property with all enumerations already attached. 4044ba55f2f5SFrançois Tigeot * 4045ba55f2f5SFrançois Tigeot * Returns: 4046ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 4047ba55f2f5SFrançois Tigeot */ 40485718399fSFrançois Tigeot int drm_property_add_enum(struct drm_property *property, int index, 40495718399fSFrançois Tigeot uint64_t value, const char *name) 40505718399fSFrançois Tigeot { 40515718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 40525718399fSFrançois Tigeot 4053ba55f2f5SFrançois Tigeot if (!(drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 4054ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK))) 4055b5162e19SFrançois Tigeot return -EINVAL; 4056b5162e19SFrançois Tigeot 4057b5162e19SFrançois Tigeot /* 4058b5162e19SFrançois Tigeot * Bitmask enum properties have the additional constraint of values 4059b5162e19SFrançois Tigeot * from 0 to 63 4060b5162e19SFrançois Tigeot */ 4061ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK) && 4062ba55f2f5SFrançois Tigeot (value > 63)) 40635718399fSFrançois Tigeot return -EINVAL; 40645718399fSFrançois Tigeot 40652c9916cdSFrançois Tigeot if (!list_empty(&property->enum_list)) { 40662c9916cdSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_list, head) { 40675718399fSFrançois Tigeot if (prop_enum->value == value) { 40685718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 40695718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 40705718399fSFrançois Tigeot return 0; 40715718399fSFrançois Tigeot } 40725718399fSFrançois Tigeot } 40735718399fSFrançois Tigeot } 40745718399fSFrançois Tigeot 40754dbb207bSFrançois Tigeot prop_enum = kzalloc(sizeof(struct drm_property_enum), GFP_KERNEL); 4076b5162e19SFrançois Tigeot if (!prop_enum) 4077b5162e19SFrançois Tigeot return -ENOMEM; 40785718399fSFrançois Tigeot 40795718399fSFrançois Tigeot strncpy(prop_enum->name, name, DRM_PROP_NAME_LEN); 40805718399fSFrançois Tigeot prop_enum->name[DRM_PROP_NAME_LEN-1] = '\0'; 40815718399fSFrançois Tigeot prop_enum->value = value; 40825718399fSFrançois Tigeot 40835718399fSFrançois Tigeot property->values[index] = value; 40842c9916cdSFrançois Tigeot list_add_tail(&prop_enum->head, &property->enum_list); 40855718399fSFrançois Tigeot return 0; 40865718399fSFrançois Tigeot } 4087b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_add_enum); 40885718399fSFrançois Tigeot 4089ba55f2f5SFrançois Tigeot /** 4090ba55f2f5SFrançois Tigeot * drm_property_destroy - destroy a drm property 4091ba55f2f5SFrançois Tigeot * @dev: drm device 4092ba55f2f5SFrançois Tigeot * @property: property to destry 4093ba55f2f5SFrançois Tigeot * 4094ba55f2f5SFrançois Tigeot * This function frees a property including any attached resources like 4095ba55f2f5SFrançois Tigeot * enumeration values. 4096ba55f2f5SFrançois Tigeot */ 40975718399fSFrançois Tigeot void drm_property_destroy(struct drm_device *dev, struct drm_property *property) 40985718399fSFrançois Tigeot { 40995718399fSFrançois Tigeot struct drm_property_enum *prop_enum, *pt; 41005718399fSFrançois Tigeot 41012c9916cdSFrançois Tigeot list_for_each_entry_safe(prop_enum, pt, &property->enum_list, head) { 41025718399fSFrançois Tigeot list_del(&prop_enum->head); 41034dbb207bSFrançois Tigeot kfree(prop_enum); 41045718399fSFrançois Tigeot } 41055718399fSFrançois Tigeot 41065718399fSFrançois Tigeot if (property->num_values) 41074dbb207bSFrançois Tigeot kfree(property->values); 41088621f407SFrançois Tigeot drm_mode_object_unregister(dev, &property->base); 41095718399fSFrançois Tigeot list_del(&property->head); 41104dbb207bSFrançois Tigeot kfree(property); 41115718399fSFrançois Tigeot } 4112b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_property_destroy); 41135718399fSFrançois Tigeot 4114ba55f2f5SFrançois Tigeot /** 4115ba55f2f5SFrançois Tigeot * drm_object_attach_property - attach a property to a modeset object 4116ba55f2f5SFrançois Tigeot * @obj: drm modeset object 4117ba55f2f5SFrançois Tigeot * @property: property to attach 4118ba55f2f5SFrançois Tigeot * @init_val: initial value of the property 4119ba55f2f5SFrançois Tigeot * 4120ba55f2f5SFrançois Tigeot * This attaches the given property to the modeset object with the given initial 4121ba55f2f5SFrançois Tigeot * value. Currently this function cannot fail since the properties are stored in 4122ba55f2f5SFrançois Tigeot * a statically sized array. 4123ba55f2f5SFrançois Tigeot */ 4124b5162e19SFrançois Tigeot void drm_object_attach_property(struct drm_mode_object *obj, 4125b5162e19SFrançois Tigeot struct drm_property *property, 4126b5162e19SFrançois Tigeot uint64_t init_val) 4127b5162e19SFrançois Tigeot { 4128b5162e19SFrançois Tigeot int count = obj->properties->count; 4129b5162e19SFrançois Tigeot 4130b5162e19SFrançois Tigeot if (count == DRM_OBJECT_MAX_PROPERTY) { 4131b5162e19SFrançois Tigeot WARN(1, "Failed to attach object property (type: 0x%x). Please " 4132b5162e19SFrançois Tigeot "increase DRM_OBJECT_MAX_PROPERTY by 1 for each time " 4133b5162e19SFrançois Tigeot "you see this message on the same object type.\n", 4134b5162e19SFrançois Tigeot obj->type); 4135b5162e19SFrançois Tigeot return; 4136b5162e19SFrançois Tigeot } 4137b5162e19SFrançois Tigeot 41382c9916cdSFrançois Tigeot obj->properties->properties[count] = property; 4139b5162e19SFrançois Tigeot obj->properties->values[count] = init_val; 4140b5162e19SFrançois Tigeot obj->properties->count++; 41412c9916cdSFrançois Tigeot if (property->flags & DRM_MODE_PROP_ATOMIC) 41422c9916cdSFrançois Tigeot obj->properties->atomic_count++; 4143b5162e19SFrançois Tigeot } 4144b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_attach_property); 4145b5162e19SFrançois Tigeot 4146ba55f2f5SFrançois Tigeot /** 4147ba55f2f5SFrançois Tigeot * drm_object_property_set_value - set the value of a property 4148ba55f2f5SFrançois Tigeot * @obj: drm mode object to set property value for 4149ba55f2f5SFrançois Tigeot * @property: property to set 4150ba55f2f5SFrançois Tigeot * @val: value the property should be set to 4151ba55f2f5SFrançois Tigeot * 4152ba55f2f5SFrançois Tigeot * This functions sets a given property on a given object. This function only 4153ba55f2f5SFrançois Tigeot * changes the software state of the property, it does not call into the 4154ba55f2f5SFrançois Tigeot * driver's ->set_property callback. 4155ba55f2f5SFrançois Tigeot * 4156ba55f2f5SFrançois Tigeot * Returns: 4157ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 4158ba55f2f5SFrançois Tigeot */ 4159b5162e19SFrançois Tigeot int drm_object_property_set_value(struct drm_mode_object *obj, 4160b5162e19SFrançois Tigeot struct drm_property *property, uint64_t val) 41615718399fSFrançois Tigeot { 41625718399fSFrançois Tigeot int i; 41635718399fSFrançois Tigeot 4164b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 41652c9916cdSFrançois Tigeot if (obj->properties->properties[i] == property) { 4166b5162e19SFrançois Tigeot obj->properties->values[i] = val; 41675718399fSFrançois Tigeot return 0; 41685718399fSFrançois Tigeot } 41695718399fSFrançois Tigeot } 41705718399fSFrançois Tigeot 41715718399fSFrançois Tigeot return -EINVAL; 41725718399fSFrançois Tigeot } 4173b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_set_value); 41745718399fSFrançois Tigeot 4175ba55f2f5SFrançois Tigeot /** 4176ba55f2f5SFrançois Tigeot * drm_object_property_get_value - retrieve the value of a property 4177ba55f2f5SFrançois Tigeot * @obj: drm mode object to get property value from 4178ba55f2f5SFrançois Tigeot * @property: property to retrieve 4179ba55f2f5SFrançois Tigeot * @val: storage for the property value 4180ba55f2f5SFrançois Tigeot * 4181ba55f2f5SFrançois Tigeot * This function retrieves the softare state of the given property for the given 4182ba55f2f5SFrançois Tigeot * property. Since there is no driver callback to retrieve the current property 4183ba55f2f5SFrançois Tigeot * value this might be out of sync with the hardware, depending upon the driver 4184ba55f2f5SFrançois Tigeot * and property. 4185ba55f2f5SFrançois Tigeot * 4186ba55f2f5SFrançois Tigeot * Returns: 4187ba55f2f5SFrançois Tigeot * Zero on success, error code on failure. 4188ba55f2f5SFrançois Tigeot */ 4189b5162e19SFrançois Tigeot int drm_object_property_get_value(struct drm_mode_object *obj, 41905718399fSFrançois Tigeot struct drm_property *property, uint64_t *val) 41915718399fSFrançois Tigeot { 41925718399fSFrançois Tigeot int i; 41935718399fSFrançois Tigeot 41942c9916cdSFrançois Tigeot /* read-only properties bypass atomic mechanism and still store 41952c9916cdSFrançois Tigeot * their value in obj->properties->values[].. mostly to avoid 41962c9916cdSFrançois Tigeot * having to deal w/ EDID and similar props in atomic paths: 41972c9916cdSFrançois Tigeot */ 41982c9916cdSFrançois Tigeot if (drm_core_check_feature(property->dev, DRIVER_ATOMIC) && 41992c9916cdSFrançois Tigeot !(property->flags & DRM_MODE_PROP_IMMUTABLE)) 42002c9916cdSFrançois Tigeot return drm_atomic_get_property(obj, property, val); 42012c9916cdSFrançois Tigeot 4202b5162e19SFrançois Tigeot for (i = 0; i < obj->properties->count; i++) { 42032c9916cdSFrançois Tigeot if (obj->properties->properties[i] == property) { 4204b5162e19SFrançois Tigeot *val = obj->properties->values[i]; 4205b5162e19SFrançois Tigeot return 0; 42065718399fSFrançois Tigeot } 42075718399fSFrançois Tigeot } 42085718399fSFrançois Tigeot 42095718399fSFrançois Tigeot return -EINVAL; 42105718399fSFrançois Tigeot } 4211b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_object_property_get_value); 42125718399fSFrançois Tigeot 4213ba55f2f5SFrançois Tigeot /** 42142c9916cdSFrançois Tigeot * drm_mode_getproperty_ioctl - get the property metadata 4215ba55f2f5SFrançois Tigeot * @dev: DRM device 4216ba55f2f5SFrançois Tigeot * @data: ioctl data 4217ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4218ba55f2f5SFrançois Tigeot * 42192c9916cdSFrançois Tigeot * This function retrieves the metadata for a given property, like the different 42202c9916cdSFrançois Tigeot * possible values for an enum property or the limits for a range property. 42212c9916cdSFrançois Tigeot * 42222c9916cdSFrançois Tigeot * Blob properties are special 4223ba55f2f5SFrançois Tigeot * 4224ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4225ba55f2f5SFrançois Tigeot * 4226ba55f2f5SFrançois Tigeot * Returns: 42272c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 4228ba55f2f5SFrançois Tigeot */ 42295718399fSFrançois Tigeot int drm_mode_getproperty_ioctl(struct drm_device *dev, 42305718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 42315718399fSFrançois Tigeot { 42325718399fSFrançois Tigeot struct drm_mode_get_property *out_resp = data; 42335718399fSFrançois Tigeot struct drm_property *property; 42345718399fSFrançois Tigeot int enum_count = 0; 42355718399fSFrançois Tigeot int value_count = 0; 42365718399fSFrançois Tigeot int ret = 0, i; 42375718399fSFrançois Tigeot int copied; 42385718399fSFrançois Tigeot struct drm_property_enum *prop_enum; 42395718399fSFrançois Tigeot struct drm_mode_property_enum __user *enum_ptr; 4240b5162e19SFrançois Tigeot uint64_t __user *values_ptr; 42415718399fSFrançois Tigeot 42425718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 42435718399fSFrançois Tigeot return -EINVAL; 42445718399fSFrançois Tigeot 42454dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 4246ba55f2f5SFrançois Tigeot property = drm_property_find(dev, out_resp->prop_id); 4247ba55f2f5SFrançois Tigeot if (!property) { 42489edbd4a0SFrançois Tigeot ret = -ENOENT; 42495718399fSFrançois Tigeot goto done; 42505718399fSFrançois Tigeot } 42515718399fSFrançois Tigeot 4252ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 4253ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 42542c9916cdSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_list, head) 42555718399fSFrançois Tigeot enum_count++; 42565718399fSFrançois Tigeot } 42575718399fSFrançois Tigeot 42585718399fSFrançois Tigeot value_count = property->num_values; 42595718399fSFrançois Tigeot 42605718399fSFrançois Tigeot strncpy(out_resp->name, property->name, DRM_PROP_NAME_LEN); 42615718399fSFrançois Tigeot out_resp->name[DRM_PROP_NAME_LEN-1] = 0; 42625718399fSFrançois Tigeot out_resp->flags = property->flags; 42635718399fSFrançois Tigeot 42645718399fSFrançois Tigeot if ((out_resp->count_values >= value_count) && value_count) { 4265b5162e19SFrançois Tigeot values_ptr = (uint64_t __user *)(unsigned long)out_resp->values_ptr; 42665718399fSFrançois Tigeot for (i = 0; i < value_count; i++) { 4267b5162e19SFrançois Tigeot if (copy_to_user(values_ptr + i, &property->values[i], sizeof(uint64_t))) { 42685718399fSFrançois Tigeot ret = -EFAULT; 42695718399fSFrançois Tigeot goto done; 42705718399fSFrançois Tigeot } 42715718399fSFrançois Tigeot } 42725718399fSFrançois Tigeot } 42735718399fSFrançois Tigeot out_resp->count_values = value_count; 42745718399fSFrançois Tigeot 4275ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_ENUM) || 4276ba55f2f5SFrançois Tigeot drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 42775718399fSFrançois Tigeot if ((out_resp->count_enum_blobs >= enum_count) && enum_count) { 42785718399fSFrançois Tigeot copied = 0; 4279b5162e19SFrançois Tigeot enum_ptr = (struct drm_mode_property_enum __user *)(unsigned long)out_resp->enum_blob_ptr; 42802c9916cdSFrançois Tigeot list_for_each_entry(prop_enum, &property->enum_list, head) { 42815718399fSFrançois Tigeot 4282b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].value, &prop_enum->value, sizeof(uint64_t))) { 42835718399fSFrançois Tigeot ret = -EFAULT; 42845718399fSFrançois Tigeot goto done; 42855718399fSFrançois Tigeot } 42865718399fSFrançois Tigeot 4287b5162e19SFrançois Tigeot if (copy_to_user(&enum_ptr[copied].name, 4288b5162e19SFrançois Tigeot &prop_enum->name, DRM_PROP_NAME_LEN)) { 42895718399fSFrançois Tigeot ret = -EFAULT; 42905718399fSFrançois Tigeot goto done; 42915718399fSFrançois Tigeot } 42925718399fSFrançois Tigeot copied++; 42935718399fSFrançois Tigeot } 42945718399fSFrançois Tigeot } 42955718399fSFrançois Tigeot out_resp->count_enum_blobs = enum_count; 42965718399fSFrançois Tigeot } 42975718399fSFrançois Tigeot 42982c9916cdSFrançois Tigeot /* 42992c9916cdSFrançois Tigeot * NOTE: The idea seems to have been to use this to read all the blob 43002c9916cdSFrançois Tigeot * property values. But nothing ever added them to the corresponding 43012c9916cdSFrançois Tigeot * list, userspace always used the special-purpose get_blob ioctl to 43022c9916cdSFrançois Tigeot * read the value for a blob property. It also doesn't make a lot of 43032c9916cdSFrançois Tigeot * sense to return values here when everything else is just metadata for 43042c9916cdSFrançois Tigeot * the property itself. 43052c9916cdSFrançois Tigeot */ 43062c9916cdSFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) 43072c9916cdSFrançois Tigeot out_resp->count_enum_blobs = 0; 43085718399fSFrançois Tigeot done: 43094dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 43105718399fSFrançois Tigeot return ret; 43115718399fSFrançois Tigeot } 43125718399fSFrançois Tigeot 43138621f407SFrançois Tigeot static void drm_property_free_blob(struct kref *kref) 43148621f407SFrançois Tigeot { 43158621f407SFrançois Tigeot struct drm_property_blob *blob = 43168621f407SFrançois Tigeot container_of(kref, struct drm_property_blob, base.refcount); 43178621f407SFrançois Tigeot 43188621f407SFrançois Tigeot mutex_lock(&blob->dev->mode_config.blob_lock); 43198621f407SFrançois Tigeot list_del(&blob->head_global); 43208621f407SFrançois Tigeot mutex_unlock(&blob->dev->mode_config.blob_lock); 43218621f407SFrançois Tigeot 43228621f407SFrançois Tigeot drm_mode_object_unregister(blob->dev, &blob->base); 43238621f407SFrançois Tigeot 43248621f407SFrançois Tigeot kfree(blob); 43258621f407SFrançois Tigeot } 43268621f407SFrançois Tigeot 432719c468b4SFrançois Tigeot /** 432819c468b4SFrançois Tigeot * drm_property_create_blob - Create new blob property 432919c468b4SFrançois Tigeot * 433019c468b4SFrançois Tigeot * Creates a new blob property for a specified DRM device, optionally 433119c468b4SFrançois Tigeot * copying data. 433219c468b4SFrançois Tigeot * 433319c468b4SFrançois Tigeot * @dev: DRM device to create property for 433419c468b4SFrançois Tigeot * @length: Length to allocate for blob data 433519c468b4SFrançois Tigeot * @data: If specified, copies data into blob 433619c468b4SFrançois Tigeot * 433719c468b4SFrançois Tigeot * Returns: 433819c468b4SFrançois Tigeot * New blob property with a single reference on success, or an ERR_PTR 433919c468b4SFrançois Tigeot * value on failure. 434019c468b4SFrançois Tigeot */ 434119c468b4SFrançois Tigeot struct drm_property_blob * 43422c9916cdSFrançois Tigeot drm_property_create_blob(struct drm_device *dev, size_t length, 43432c9916cdSFrançois Tigeot const void *data) 43445718399fSFrançois Tigeot { 43455718399fSFrançois Tigeot struct drm_property_blob *blob; 43465718399fSFrançois Tigeot int ret; 43475718399fSFrançois Tigeot 4348a05eeebfSFrançois Tigeot if (!length || length > ULONG_MAX - sizeof(struct drm_property_blob)) 434919c468b4SFrançois Tigeot return ERR_PTR(-EINVAL); 43505718399fSFrançois Tigeot 43514dbb207bSFrançois Tigeot blob = kzalloc(sizeof(struct drm_property_blob)+length, GFP_KERNEL); 4352b5162e19SFrançois Tigeot if (!blob) 435319c468b4SFrançois Tigeot return ERR_PTR(-ENOMEM); 435419c468b4SFrançois Tigeot 435519c468b4SFrançois Tigeot /* This must be explicitly initialised, so we can safely call list_del 435619c468b4SFrançois Tigeot * on it in the removal handler, even if it isn't in a file list. */ 435719c468b4SFrançois Tigeot INIT_LIST_HEAD(&blob->head_file); 435819c468b4SFrançois Tigeot blob->length = length; 435919c468b4SFrançois Tigeot blob->dev = dev; 436019c468b4SFrançois Tigeot 436119c468b4SFrançois Tigeot if (data) 436219c468b4SFrançois Tigeot memcpy(blob->data, data, length); 436319c468b4SFrançois Tigeot 43648621f407SFrançois Tigeot ret = drm_mode_object_get_reg(dev, &blob->base, DRM_MODE_OBJECT_BLOB, 43658621f407SFrançois Tigeot true, drm_property_free_blob); 43665718399fSFrançois Tigeot if (ret) { 43674dbb207bSFrançois Tigeot kfree(blob); 436819c468b4SFrançois Tigeot return ERR_PTR(-EINVAL); 43695718399fSFrançois Tigeot } 43705718399fSFrançois Tigeot 43718621f407SFrançois Tigeot mutex_lock(&dev->mode_config.blob_lock); 437219c468b4SFrançois Tigeot list_add_tail(&blob->head_global, 437319c468b4SFrançois Tigeot &dev->mode_config.property_blob_list); 437419c468b4SFrançois Tigeot mutex_unlock(&dev->mode_config.blob_lock); 437519c468b4SFrançois Tigeot 437619c468b4SFrançois Tigeot return blob; 437719c468b4SFrançois Tigeot } 437819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_create_blob); 437919c468b4SFrançois Tigeot 438019c468b4SFrançois Tigeot /** 438119c468b4SFrançois Tigeot * drm_property_unreference_blob - Unreference a blob property 438219c468b4SFrançois Tigeot * 438319c468b4SFrançois Tigeot * Drop a reference on a blob property. May free the object. 438419c468b4SFrançois Tigeot * 438519c468b4SFrançois Tigeot * @blob: Pointer to blob property 438619c468b4SFrançois Tigeot */ 438719c468b4SFrançois Tigeot void drm_property_unreference_blob(struct drm_property_blob *blob) 438819c468b4SFrançois Tigeot { 438919c468b4SFrançois Tigeot if (!blob) 439019c468b4SFrançois Tigeot return; 439119c468b4SFrançois Tigeot 43928621f407SFrançois Tigeot drm_mode_object_unreference(&blob->base); 439319c468b4SFrançois Tigeot } 439419c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_unreference_blob); 439519c468b4SFrançois Tigeot 439619c468b4SFrançois Tigeot /** 439719c468b4SFrançois Tigeot * drm_property_destroy_user_blobs - destroy all blobs created by this client 439819c468b4SFrançois Tigeot * @dev: DRM device 439919c468b4SFrançois Tigeot * @file_priv: destroy all blobs owned by this file handle 440019c468b4SFrançois Tigeot */ 440119c468b4SFrançois Tigeot void drm_property_destroy_user_blobs(struct drm_device *dev, 440219c468b4SFrançois Tigeot struct drm_file *file_priv) 440319c468b4SFrançois Tigeot { 440419c468b4SFrançois Tigeot struct drm_property_blob *blob, *bt; 440519c468b4SFrançois Tigeot 44068621f407SFrançois Tigeot /* 44078621f407SFrançois Tigeot * When the file gets released that means no one else can access the 44088621f407SFrançois Tigeot * blob list any more, so no need to grab dev->blob_lock. 44098621f407SFrançois Tigeot */ 441019c468b4SFrançois Tigeot list_for_each_entry_safe(blob, bt, &file_priv->blobs, head_file) { 441119c468b4SFrançois Tigeot list_del_init(&blob->head_file); 44128621f407SFrançois Tigeot drm_property_unreference_blob(blob); 441319c468b4SFrançois Tigeot } 441419c468b4SFrançois Tigeot } 441519c468b4SFrançois Tigeot 441619c468b4SFrançois Tigeot /** 441719c468b4SFrançois Tigeot * drm_property_reference_blob - Take a reference on an existing property 441819c468b4SFrançois Tigeot * 441919c468b4SFrançois Tigeot * Take a new reference on an existing blob property. 442019c468b4SFrançois Tigeot * 442119c468b4SFrançois Tigeot * @blob: Pointer to blob property 442219c468b4SFrançois Tigeot */ 442319c468b4SFrançois Tigeot struct drm_property_blob *drm_property_reference_blob(struct drm_property_blob *blob) 442419c468b4SFrançois Tigeot { 44258621f407SFrançois Tigeot drm_mode_object_reference(&blob->base); 442619c468b4SFrançois Tigeot return blob; 442719c468b4SFrançois Tigeot } 442819c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_reference_blob); 442919c468b4SFrançois Tigeot 443019c468b4SFrançois Tigeot /** 443119c468b4SFrançois Tigeot * drm_property_lookup_blob - look up a blob property and take a reference 443219c468b4SFrançois Tigeot * @dev: drm device 443319c468b4SFrançois Tigeot * @id: id of the blob property 443419c468b4SFrançois Tigeot * 443519c468b4SFrançois Tigeot * If successful, this takes an additional reference to the blob property. 443619c468b4SFrançois Tigeot * callers need to make sure to eventually unreference the returned property 443719c468b4SFrançois Tigeot * again, using @drm_property_unreference_blob. 443819c468b4SFrançois Tigeot */ 443919c468b4SFrançois Tigeot struct drm_property_blob *drm_property_lookup_blob(struct drm_device *dev, 444019c468b4SFrançois Tigeot uint32_t id) 44415718399fSFrançois Tigeot { 44428621f407SFrançois Tigeot struct drm_mode_object *obj; 44438621f407SFrançois Tigeot struct drm_property_blob *blob = NULL; 444419c468b4SFrançois Tigeot 44458621f407SFrançois Tigeot obj = _object_find(dev, id, DRM_MODE_OBJECT_BLOB); 44468621f407SFrançois Tigeot if (obj) 44478621f407SFrançois Tigeot blob = obj_to_blob(obj); 444819c468b4SFrançois Tigeot return blob; 444919c468b4SFrançois Tigeot } 445019c468b4SFrançois Tigeot EXPORT_SYMBOL(drm_property_lookup_blob); 445119c468b4SFrançois Tigeot 445219c468b4SFrançois Tigeot /** 445319c468b4SFrançois Tigeot * drm_property_replace_global_blob - atomically replace existing blob property 445419c468b4SFrançois Tigeot * @dev: drm device 445519c468b4SFrançois Tigeot * @replace: location of blob property pointer to be replaced 445619c468b4SFrançois Tigeot * @length: length of data for new blob, or 0 for no data 445719c468b4SFrançois Tigeot * @data: content for new blob, or NULL for no data 445819c468b4SFrançois Tigeot * @obj_holds_id: optional object for property holding blob ID 445919c468b4SFrançois Tigeot * @prop_holds_id: optional property holding blob ID 446019c468b4SFrançois Tigeot * @return 0 on success or error on failure 446119c468b4SFrançois Tigeot * 446219c468b4SFrançois Tigeot * This function will atomically replace a global property in the blob list, 446319c468b4SFrançois Tigeot * optionally updating a property which holds the ID of that property. It is 446419c468b4SFrançois Tigeot * guaranteed to be atomic: no caller will be allowed to see intermediate 446519c468b4SFrançois Tigeot * results, and either the entire operation will succeed and clean up the 446619c468b4SFrançois Tigeot * previous property, or it will fail and the state will be unchanged. 446719c468b4SFrançois Tigeot * 446819c468b4SFrançois Tigeot * If length is 0 or data is NULL, no new blob will be created, and the holding 446919c468b4SFrançois Tigeot * property, if specified, will be set to 0. 447019c468b4SFrançois Tigeot * 447119c468b4SFrançois Tigeot * Access to the replace pointer is assumed to be protected by the caller, e.g. 447219c468b4SFrançois Tigeot * by holding the relevant modesetting object lock for its parent. 447319c468b4SFrançois Tigeot * 447419c468b4SFrançois Tigeot * For example, a drm_connector has a 'PATH' property, which contains the ID 447519c468b4SFrançois Tigeot * of a blob property with the value of the MST path information. Calling this 447619c468b4SFrançois Tigeot * function with replace pointing to the connector's path_blob_ptr, length and 447719c468b4SFrançois Tigeot * data set for the new path information, obj_holds_id set to the connector's 447819c468b4SFrançois Tigeot * base object, and prop_holds_id set to the path property name, will perform 447919c468b4SFrançois Tigeot * a completely atomic update. The access to path_blob_ptr is protected by the 448019c468b4SFrançois Tigeot * caller holding a lock on the connector. 448119c468b4SFrançois Tigeot */ 448219c468b4SFrançois Tigeot static int drm_property_replace_global_blob(struct drm_device *dev, 448319c468b4SFrançois Tigeot struct drm_property_blob **replace, 448419c468b4SFrançois Tigeot size_t length, 448519c468b4SFrançois Tigeot const void *data, 448619c468b4SFrançois Tigeot struct drm_mode_object *obj_holds_id, 448719c468b4SFrançois Tigeot struct drm_property *prop_holds_id) 448819c468b4SFrançois Tigeot { 448919c468b4SFrançois Tigeot struct drm_property_blob *new_blob = NULL; 449019c468b4SFrançois Tigeot struct drm_property_blob *old_blob = NULL; 449119c468b4SFrançois Tigeot int ret; 449219c468b4SFrançois Tigeot 449319c468b4SFrançois Tigeot WARN_ON(replace == NULL); 449419c468b4SFrançois Tigeot 449519c468b4SFrançois Tigeot old_blob = *replace; 449619c468b4SFrançois Tigeot 449719c468b4SFrançois Tigeot if (length && data) { 449819c468b4SFrançois Tigeot new_blob = drm_property_create_blob(dev, length, data); 449919c468b4SFrançois Tigeot if (IS_ERR(new_blob)) 450019c468b4SFrançois Tigeot return PTR_ERR(new_blob); 450119c468b4SFrançois Tigeot } 450219c468b4SFrançois Tigeot 450319c468b4SFrançois Tigeot /* This does not need to be synchronised with blob_lock, as the 450419c468b4SFrançois Tigeot * get_properties ioctl locks all modesetting objects, and 450519c468b4SFrançois Tigeot * obj_holds_id must be locked before calling here, so we cannot 450619c468b4SFrançois Tigeot * have its value out of sync with the list membership modified 450719c468b4SFrançois Tigeot * below under blob_lock. */ 450819c468b4SFrançois Tigeot if (obj_holds_id) { 450919c468b4SFrançois Tigeot ret = drm_object_property_set_value(obj_holds_id, 451019c468b4SFrançois Tigeot prop_holds_id, 451119c468b4SFrançois Tigeot new_blob ? 451219c468b4SFrançois Tigeot new_blob->base.id : 0); 451319c468b4SFrançois Tigeot if (ret != 0) 451419c468b4SFrançois Tigeot goto err_created; 451519c468b4SFrançois Tigeot } 451619c468b4SFrançois Tigeot 451719c468b4SFrançois Tigeot drm_property_unreference_blob(old_blob); 451819c468b4SFrançois Tigeot *replace = new_blob; 451919c468b4SFrançois Tigeot 452019c468b4SFrançois Tigeot return 0; 452119c468b4SFrançois Tigeot 452219c468b4SFrançois Tigeot err_created: 452319c468b4SFrançois Tigeot drm_property_unreference_blob(new_blob); 452419c468b4SFrançois Tigeot return ret; 45255718399fSFrançois Tigeot } 45265718399fSFrançois Tigeot 4527ba55f2f5SFrançois Tigeot /** 4528ba55f2f5SFrançois Tigeot * drm_mode_getblob_ioctl - get the contents of a blob property value 4529ba55f2f5SFrançois Tigeot * @dev: DRM device 4530ba55f2f5SFrançois Tigeot * @data: ioctl data 4531ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4532ba55f2f5SFrançois Tigeot * 4533ba55f2f5SFrançois Tigeot * This function retrieves the contents of a blob property. The value stored in 4534ba55f2f5SFrançois Tigeot * an object's blob property is just a normal modeset object id. 4535ba55f2f5SFrançois Tigeot * 4536ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4537ba55f2f5SFrançois Tigeot * 4538ba55f2f5SFrançois Tigeot * Returns: 45392c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 4540ba55f2f5SFrançois Tigeot */ 45415718399fSFrançois Tigeot int drm_mode_getblob_ioctl(struct drm_device *dev, 45425718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 45435718399fSFrançois Tigeot { 45445718399fSFrançois Tigeot struct drm_mode_get_blob *out_resp = data; 45455718399fSFrançois Tigeot struct drm_property_blob *blob; 45465718399fSFrançois Tigeot int ret = 0; 4547b5162e19SFrançois Tigeot void __user *blob_ptr; 45485718399fSFrançois Tigeot 45495718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 45505718399fSFrançois Tigeot return -EINVAL; 45515718399fSFrançois Tigeot 45528621f407SFrançois Tigeot blob = drm_property_lookup_blob(dev, out_resp->blob_id); 45538621f407SFrançois Tigeot if (!blob) 45548621f407SFrançois Tigeot return -ENOENT; 45555718399fSFrançois Tigeot 45565718399fSFrançois Tigeot if (out_resp->length == blob->length) { 4557b5162e19SFrançois Tigeot blob_ptr = (void __user *)(unsigned long)out_resp->data; 4558b5162e19SFrançois Tigeot if (copy_to_user(blob_ptr, blob->data, blob->length)) { 45595718399fSFrançois Tigeot ret = -EFAULT; 45608621f407SFrançois Tigeot goto unref; 45615718399fSFrançois Tigeot } 45625718399fSFrançois Tigeot } 45635718399fSFrançois Tigeot out_resp->length = blob->length; 45648621f407SFrançois Tigeot unref: 45658621f407SFrançois Tigeot drm_property_unreference_blob(blob); 45665718399fSFrançois Tigeot 45675718399fSFrançois Tigeot return ret; 45685718399fSFrançois Tigeot } 45695718399fSFrançois Tigeot 45702c9916cdSFrançois Tigeot /** 457119c468b4SFrançois Tigeot * drm_mode_createblob_ioctl - create a new blob property 457219c468b4SFrançois Tigeot * @dev: DRM device 457319c468b4SFrançois Tigeot * @data: ioctl data 457419c468b4SFrançois Tigeot * @file_priv: DRM file info 457519c468b4SFrançois Tigeot * 457619c468b4SFrançois Tigeot * This function creates a new blob property with user-defined values. In order 457719c468b4SFrançois Tigeot * to give us sensible validation and checking when creating, rather than at 457819c468b4SFrançois Tigeot * every potential use, we also require a type to be provided upfront. 457919c468b4SFrançois Tigeot * 458019c468b4SFrançois Tigeot * Called by the user via ioctl. 458119c468b4SFrançois Tigeot * 458219c468b4SFrançois Tigeot * Returns: 458319c468b4SFrançois Tigeot * Zero on success, negative errno on failure. 458419c468b4SFrançois Tigeot */ 458519c468b4SFrançois Tigeot int drm_mode_createblob_ioctl(struct drm_device *dev, 458619c468b4SFrançois Tigeot void *data, struct drm_file *file_priv) 458719c468b4SFrançois Tigeot { 458819c468b4SFrançois Tigeot struct drm_mode_create_blob *out_resp = data; 458919c468b4SFrançois Tigeot struct drm_property_blob *blob; 459019c468b4SFrançois Tigeot void __user *blob_ptr; 459119c468b4SFrançois Tigeot int ret = 0; 459219c468b4SFrançois Tigeot 459319c468b4SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 459419c468b4SFrançois Tigeot return -EINVAL; 459519c468b4SFrançois Tigeot 459619c468b4SFrançois Tigeot blob = drm_property_create_blob(dev, out_resp->length, NULL); 459719c468b4SFrançois Tigeot if (IS_ERR(blob)) 459819c468b4SFrançois Tigeot return PTR_ERR(blob); 459919c468b4SFrançois Tigeot 460019c468b4SFrançois Tigeot blob_ptr = (void __user *)(unsigned long)out_resp->data; 460119c468b4SFrançois Tigeot if (copy_from_user(blob->data, blob_ptr, out_resp->length)) { 460219c468b4SFrançois Tigeot ret = -EFAULT; 460319c468b4SFrançois Tigeot goto out_blob; 460419c468b4SFrançois Tigeot } 460519c468b4SFrançois Tigeot 460619c468b4SFrançois Tigeot /* Dropping the lock between create_blob and our access here is safe 460719c468b4SFrançois Tigeot * as only the same file_priv can remove the blob; at this point, it is 460819c468b4SFrançois Tigeot * not associated with any file_priv. */ 460919c468b4SFrançois Tigeot mutex_lock(&dev->mode_config.blob_lock); 461019c468b4SFrançois Tigeot out_resp->blob_id = blob->base.id; 4611a05eeebfSFrançois Tigeot list_add_tail(&blob->head_file, &file_priv->blobs); 461219c468b4SFrançois Tigeot mutex_unlock(&dev->mode_config.blob_lock); 461319c468b4SFrançois Tigeot 461419c468b4SFrançois Tigeot return 0; 461519c468b4SFrançois Tigeot 461619c468b4SFrançois Tigeot out_blob: 461719c468b4SFrançois Tigeot drm_property_unreference_blob(blob); 461819c468b4SFrançois Tigeot return ret; 461919c468b4SFrançois Tigeot } 462019c468b4SFrançois Tigeot 462119c468b4SFrançois Tigeot /** 462219c468b4SFrançois Tigeot * drm_mode_destroyblob_ioctl - destroy a user blob property 462319c468b4SFrançois Tigeot * @dev: DRM device 462419c468b4SFrançois Tigeot * @data: ioctl data 462519c468b4SFrançois Tigeot * @file_priv: DRM file info 462619c468b4SFrançois Tigeot * 462719c468b4SFrançois Tigeot * Destroy an existing user-defined blob property. 462819c468b4SFrançois Tigeot * 462919c468b4SFrançois Tigeot * Called by the user via ioctl. 463019c468b4SFrançois Tigeot * 463119c468b4SFrançois Tigeot * Returns: 463219c468b4SFrançois Tigeot * Zero on success, negative errno on failure. 463319c468b4SFrançois Tigeot */ 463419c468b4SFrançois Tigeot int drm_mode_destroyblob_ioctl(struct drm_device *dev, 463519c468b4SFrançois Tigeot void *data, struct drm_file *file_priv) 463619c468b4SFrançois Tigeot { 463719c468b4SFrançois Tigeot struct drm_mode_destroy_blob *out_resp = data; 463819c468b4SFrançois Tigeot struct drm_property_blob *blob = NULL, *bt; 463919c468b4SFrançois Tigeot bool found = false; 464019c468b4SFrançois Tigeot int ret = 0; 464119c468b4SFrançois Tigeot 464219c468b4SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 464319c468b4SFrançois Tigeot return -EINVAL; 464419c468b4SFrançois Tigeot 46458621f407SFrançois Tigeot blob = drm_property_lookup_blob(dev, out_resp->blob_id); 46468621f407SFrançois Tigeot if (!blob) 46478621f407SFrançois Tigeot return -ENOENT; 464819c468b4SFrançois Tigeot 46498621f407SFrançois Tigeot mutex_lock(&dev->mode_config.blob_lock); 465019c468b4SFrançois Tigeot /* Ensure the property was actually created by this user. */ 465119c468b4SFrançois Tigeot list_for_each_entry(bt, &file_priv->blobs, head_file) { 465219c468b4SFrançois Tigeot if (bt == blob) { 465319c468b4SFrançois Tigeot found = true; 465419c468b4SFrançois Tigeot break; 465519c468b4SFrançois Tigeot } 465619c468b4SFrançois Tigeot } 465719c468b4SFrançois Tigeot 465819c468b4SFrançois Tigeot if (!found) { 465919c468b4SFrançois Tigeot ret = -EPERM; 466019c468b4SFrançois Tigeot goto err; 466119c468b4SFrançois Tigeot } 466219c468b4SFrançois Tigeot 466319c468b4SFrançois Tigeot /* We must drop head_file here, because we may not be the last 466419c468b4SFrançois Tigeot * reference on the blob. */ 466519c468b4SFrançois Tigeot list_del_init(&blob->head_file); 466619c468b4SFrançois Tigeot mutex_unlock(&dev->mode_config.blob_lock); 466719c468b4SFrançois Tigeot 46688621f407SFrançois Tigeot /* One reference from lookup, and one from the filp. */ 46698621f407SFrançois Tigeot drm_property_unreference_blob(blob); 46708621f407SFrançois Tigeot drm_property_unreference_blob(blob); 46718621f407SFrançois Tigeot 467219c468b4SFrançois Tigeot return 0; 467319c468b4SFrançois Tigeot 467419c468b4SFrançois Tigeot err: 467519c468b4SFrançois Tigeot mutex_unlock(&dev->mode_config.blob_lock); 46768621f407SFrançois Tigeot drm_property_unreference_blob(blob); 46778621f407SFrançois Tigeot 467819c468b4SFrançois Tigeot return ret; 467919c468b4SFrançois Tigeot } 468019c468b4SFrançois Tigeot 468119c468b4SFrançois Tigeot /** 46822c9916cdSFrançois Tigeot * drm_mode_connector_set_path_property - set tile property on connector 46832c9916cdSFrançois Tigeot * @connector: connector to set property on. 468419c468b4SFrançois Tigeot * @path: path to use for property; must not be NULL. 46852c9916cdSFrançois Tigeot * 46862c9916cdSFrançois Tigeot * This creates a property to expose to userspace to specify a 46872c9916cdSFrançois Tigeot * connector path. This is mainly used for DisplayPort MST where 46882c9916cdSFrançois Tigeot * connectors have a topology and we want to allow userspace to give 46892c9916cdSFrançois Tigeot * them more meaningful names. 46902c9916cdSFrançois Tigeot * 46912c9916cdSFrançois Tigeot * Returns: 46922c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 46932c9916cdSFrançois Tigeot */ 469424edb884SFrançois Tigeot int drm_mode_connector_set_path_property(struct drm_connector *connector, 46952c9916cdSFrançois Tigeot const char *path) 469624edb884SFrançois Tigeot { 469724edb884SFrançois Tigeot struct drm_device *dev = connector->dev; 46982c9916cdSFrançois Tigeot int ret; 469924edb884SFrançois Tigeot 470019c468b4SFrançois Tigeot ret = drm_property_replace_global_blob(dev, 470119c468b4SFrançois Tigeot &connector->path_blob_ptr, 470219c468b4SFrançois Tigeot strlen(path) + 1, 470319c468b4SFrançois Tigeot path, 470419c468b4SFrançois Tigeot &connector->base, 470519c468b4SFrançois Tigeot dev->mode_config.path_property); 470624edb884SFrançois Tigeot return ret; 470724edb884SFrançois Tigeot } 470824edb884SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_path_property); 470924edb884SFrançois Tigeot 4710ba55f2f5SFrançois Tigeot /** 47112c9916cdSFrançois Tigeot * drm_mode_connector_set_tile_property - set tile property on connector 47122c9916cdSFrançois Tigeot * @connector: connector to set property on. 47132c9916cdSFrançois Tigeot * 47142c9916cdSFrançois Tigeot * This looks up the tile information for a connector, and creates a 47152c9916cdSFrançois Tigeot * property for userspace to parse if it exists. The property is of 47162c9916cdSFrançois Tigeot * the form of 8 integers using ':' as a separator. 47172c9916cdSFrançois Tigeot * 47182c9916cdSFrançois Tigeot * Returns: 47192c9916cdSFrançois Tigeot * Zero on success, errno on failure. 47202c9916cdSFrançois Tigeot */ 47212c9916cdSFrançois Tigeot int drm_mode_connector_set_tile_property(struct drm_connector *connector) 47222c9916cdSFrançois Tigeot { 47232c9916cdSFrançois Tigeot struct drm_device *dev = connector->dev; 47242c9916cdSFrançois Tigeot char tile[256]; 472519c468b4SFrançois Tigeot int ret; 47262c9916cdSFrançois Tigeot 47272c9916cdSFrançois Tigeot if (!connector->has_tile) { 472819c468b4SFrançois Tigeot ret = drm_property_replace_global_blob(dev, 472919c468b4SFrançois Tigeot &connector->tile_blob_ptr, 473019c468b4SFrançois Tigeot 0, 473119c468b4SFrançois Tigeot NULL, 473219c468b4SFrançois Tigeot &connector->base, 473319c468b4SFrançois Tigeot dev->mode_config.tile_property); 47342c9916cdSFrançois Tigeot return ret; 47352c9916cdSFrançois Tigeot } 47362c9916cdSFrançois Tigeot 47372c9916cdSFrançois Tigeot ksnprintf(tile, 256, "%d:%d:%d:%d:%d:%d:%d:%d", 47382c9916cdSFrançois Tigeot connector->tile_group->id, connector->tile_is_single_monitor, 47392c9916cdSFrançois Tigeot connector->num_h_tile, connector->num_v_tile, 47402c9916cdSFrançois Tigeot connector->tile_h_loc, connector->tile_v_loc, 47412c9916cdSFrançois Tigeot connector->tile_h_size, connector->tile_v_size); 47422c9916cdSFrançois Tigeot 474319c468b4SFrançois Tigeot ret = drm_property_replace_global_blob(dev, 474419c468b4SFrançois Tigeot &connector->tile_blob_ptr, 474519c468b4SFrançois Tigeot strlen(tile) + 1, 474619c468b4SFrançois Tigeot tile, 474719c468b4SFrançois Tigeot &connector->base, 474819c468b4SFrançois Tigeot dev->mode_config.tile_property); 47492c9916cdSFrançois Tigeot return ret; 47502c9916cdSFrançois Tigeot } 47512c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_set_tile_property); 47522c9916cdSFrançois Tigeot 47532c9916cdSFrançois Tigeot /** 4754ba55f2f5SFrançois Tigeot * drm_mode_connector_update_edid_property - update the edid property of a connector 4755ba55f2f5SFrançois Tigeot * @connector: drm connector 4756ba55f2f5SFrançois Tigeot * @edid: new value of the edid property 4757ba55f2f5SFrançois Tigeot * 4758ba55f2f5SFrançois Tigeot * This function creates a new blob modeset object and assigns its id to the 4759ba55f2f5SFrançois Tigeot * connector's edid property. 4760ba55f2f5SFrançois Tigeot * 4761ba55f2f5SFrançois Tigeot * Returns: 47622c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 4763ba55f2f5SFrançois Tigeot */ 47645718399fSFrançois Tigeot int drm_mode_connector_update_edid_property(struct drm_connector *connector, 47652c9916cdSFrançois Tigeot const struct edid *edid) 47665718399fSFrançois Tigeot { 47675718399fSFrançois Tigeot struct drm_device *dev = connector->dev; 476819c468b4SFrançois Tigeot size_t size = 0; 47692c9916cdSFrançois Tigeot int ret; 47705718399fSFrançois Tigeot 477124edb884SFrançois Tigeot /* ignore requests to set edid when overridden */ 477224edb884SFrançois Tigeot if (connector->override_edid) 477324edb884SFrançois Tigeot return 0; 477424edb884SFrançois Tigeot 477519c468b4SFrançois Tigeot if (edid) 47765718399fSFrançois Tigeot size = EDID_LENGTH * (1 + edid->extensions); 47775718399fSFrançois Tigeot 477819c468b4SFrançois Tigeot ret = drm_property_replace_global_blob(dev, 477919c468b4SFrançois Tigeot &connector->edid_blob_ptr, 478019c468b4SFrançois Tigeot size, 478119c468b4SFrançois Tigeot edid, 478219c468b4SFrançois Tigeot &connector->base, 478319c468b4SFrançois Tigeot dev->mode_config.edid_property); 47845718399fSFrançois Tigeot return ret; 47855718399fSFrançois Tigeot } 4786b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_update_edid_property); 4787b5162e19SFrançois Tigeot 47882c9916cdSFrançois Tigeot /* Some properties could refer to dynamic refcnt'd objects, or things that 47892c9916cdSFrançois Tigeot * need special locking to handle lifetime issues (ie. to ensure the prop 47902c9916cdSFrançois Tigeot * value doesn't become invalid part way through the property update due to 47912c9916cdSFrançois Tigeot * race). The value returned by reference via 'obj' should be passed back 47922c9916cdSFrançois Tigeot * to drm_property_change_valid_put() after the property is set (and the 47932c9916cdSFrançois Tigeot * object to which the property is attached has a chance to take it's own 47942c9916cdSFrançois Tigeot * reference). 47952c9916cdSFrançois Tigeot */ 47962c9916cdSFrançois Tigeot bool drm_property_change_valid_get(struct drm_property *property, 47972c9916cdSFrançois Tigeot uint64_t value, struct drm_mode_object **ref) 4798b5162e19SFrançois Tigeot { 47992c9916cdSFrançois Tigeot int i; 48002c9916cdSFrançois Tigeot 4801b5162e19SFrançois Tigeot if (property->flags & DRM_MODE_PROP_IMMUTABLE) 4802b5162e19SFrançois Tigeot return false; 4803ba55f2f5SFrançois Tigeot 48042c9916cdSFrançois Tigeot *ref = NULL; 48052c9916cdSFrançois Tigeot 4806ba55f2f5SFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_RANGE)) { 4807b5162e19SFrançois Tigeot if (value < property->values[0] || value > property->values[1]) 4808b5162e19SFrançois Tigeot return false; 4809b5162e19SFrançois Tigeot return true; 4810ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_SIGNED_RANGE)) { 4811ba55f2f5SFrançois Tigeot int64_t svalue = U642I64(value); 48122c9916cdSFrançois Tigeot 4813ba55f2f5SFrançois Tigeot if (svalue < U642I64(property->values[0]) || 4814ba55f2f5SFrançois Tigeot svalue > U642I64(property->values[1])) 4815ba55f2f5SFrançois Tigeot return false; 4816ba55f2f5SFrançois Tigeot return true; 4817ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BITMASK)) { 4818b5162e19SFrançois Tigeot uint64_t valid_mask = 0; 48192c9916cdSFrançois Tigeot 4820b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 4821b5162e19SFrançois Tigeot valid_mask |= (1ULL << property->values[i]); 4822b5162e19SFrançois Tigeot return !(value & ~valid_mask); 4823ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) { 482419c468b4SFrançois Tigeot struct drm_property_blob *blob; 482519c468b4SFrançois Tigeot 482619c468b4SFrançois Tigeot if (value == 0) 4827b5162e19SFrançois Tigeot return true; 482819c468b4SFrançois Tigeot 482919c468b4SFrançois Tigeot blob = drm_property_lookup_blob(property->dev, value); 483019c468b4SFrançois Tigeot if (blob) { 483119c468b4SFrançois Tigeot *ref = &blob->base; 483219c468b4SFrançois Tigeot return true; 483319c468b4SFrançois Tigeot } else { 483419c468b4SFrançois Tigeot return false; 483519c468b4SFrançois Tigeot } 4836ba55f2f5SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { 4837ba55f2f5SFrançois Tigeot /* a zero value for an object property translates to null: */ 4838ba55f2f5SFrançois Tigeot if (value == 0) 4839ba55f2f5SFrançois Tigeot return true; 48402c9916cdSFrançois Tigeot 48418621f407SFrançois Tigeot *ref = _object_find(property->dev, value, property->values[0]); 48428621f407SFrançois Tigeot return *ref != NULL; 48432c9916cdSFrançois Tigeot } 48442c9916cdSFrançois Tigeot 4845b5162e19SFrançois Tigeot for (i = 0; i < property->num_values; i++) 4846b5162e19SFrançois Tigeot if (property->values[i] == value) 4847b5162e19SFrançois Tigeot return true; 4848b5162e19SFrançois Tigeot return false; 4849b5162e19SFrançois Tigeot } 48502c9916cdSFrançois Tigeot 48512c9916cdSFrançois Tigeot void drm_property_change_valid_put(struct drm_property *property, 48522c9916cdSFrançois Tigeot struct drm_mode_object *ref) 48532c9916cdSFrançois Tigeot { 48542c9916cdSFrançois Tigeot if (!ref) 48552c9916cdSFrançois Tigeot return; 48562c9916cdSFrançois Tigeot 48572c9916cdSFrançois Tigeot if (drm_property_type_is(property, DRM_MODE_PROP_OBJECT)) { 48588621f407SFrançois Tigeot drm_mode_object_unreference(ref); 485919c468b4SFrançois Tigeot } else if (drm_property_type_is(property, DRM_MODE_PROP_BLOB)) 486019c468b4SFrançois Tigeot drm_property_unreference_blob(obj_to_blob(ref)); 4861b5162e19SFrançois Tigeot } 48625718399fSFrançois Tigeot 4863ba55f2f5SFrançois Tigeot /** 4864ba55f2f5SFrançois Tigeot * drm_mode_connector_property_set_ioctl - set the current value of a connector property 4865ba55f2f5SFrançois Tigeot * @dev: DRM device 4866ba55f2f5SFrançois Tigeot * @data: ioctl data 4867ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4868ba55f2f5SFrançois Tigeot * 4869ba55f2f5SFrançois Tigeot * This function sets the current value for a connectors's property. It also 4870ba55f2f5SFrançois Tigeot * calls into a driver's ->set_property callback to update the hardware state 4871ba55f2f5SFrançois Tigeot * 4872ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4873ba55f2f5SFrançois Tigeot * 4874ba55f2f5SFrançois Tigeot * Returns: 48752c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 4876ba55f2f5SFrançois Tigeot */ 48775718399fSFrançois Tigeot int drm_mode_connector_property_set_ioctl(struct drm_device *dev, 48785718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 48795718399fSFrançois Tigeot { 4880b5162e19SFrançois Tigeot struct drm_mode_connector_set_property *conn_set_prop = data; 4881b5162e19SFrançois Tigeot struct drm_mode_obj_set_property obj_set_prop = { 4882b5162e19SFrançois Tigeot .value = conn_set_prop->value, 4883b5162e19SFrançois Tigeot .prop_id = conn_set_prop->prop_id, 4884b5162e19SFrançois Tigeot .obj_id = conn_set_prop->connector_id, 4885b5162e19SFrançois Tigeot .obj_type = DRM_MODE_OBJECT_CONNECTOR 4886b5162e19SFrançois Tigeot }; 4887b5162e19SFrançois Tigeot 4888b5162e19SFrançois Tigeot /* It does all the locking and checking we need */ 4889b5162e19SFrançois Tigeot return drm_mode_obj_set_property_ioctl(dev, &obj_set_prop, file_priv); 4890b5162e19SFrançois Tigeot } 4891b5162e19SFrançois Tigeot 4892b5162e19SFrançois Tigeot static int drm_mode_connector_set_obj_prop(struct drm_mode_object *obj, 4893b5162e19SFrançois Tigeot struct drm_property *property, 4894b5162e19SFrançois Tigeot uint64_t value) 4895b5162e19SFrançois Tigeot { 4896b5162e19SFrançois Tigeot int ret = -EINVAL; 4897b5162e19SFrançois Tigeot struct drm_connector *connector = obj_to_connector(obj); 4898b5162e19SFrançois Tigeot 4899b5162e19SFrançois Tigeot /* Do DPMS ourselves */ 4900b5162e19SFrançois Tigeot if (property == connector->dev->mode_config.dpms_property) { 4901a05eeebfSFrançois Tigeot ret = (*connector->funcs->dpms)(connector, (int)value); 4902b5162e19SFrançois Tigeot } else if (connector->funcs->set_property) 4903b5162e19SFrançois Tigeot ret = connector->funcs->set_property(connector, property, value); 4904b5162e19SFrançois Tigeot 4905b5162e19SFrançois Tigeot /* store the property value if successful */ 4906b5162e19SFrançois Tigeot if (!ret) 4907b5162e19SFrançois Tigeot drm_object_property_set_value(&connector->base, property, value); 4908b5162e19SFrançois Tigeot return ret; 4909b5162e19SFrançois Tigeot } 4910b5162e19SFrançois Tigeot 4911b5162e19SFrançois Tigeot static int drm_mode_crtc_set_obj_prop(struct drm_mode_object *obj, 4912b5162e19SFrançois Tigeot struct drm_property *property, 4913b5162e19SFrançois Tigeot uint64_t value) 4914b5162e19SFrançois Tigeot { 4915b5162e19SFrançois Tigeot int ret = -EINVAL; 4916b5162e19SFrançois Tigeot struct drm_crtc *crtc = obj_to_crtc(obj); 4917b5162e19SFrançois Tigeot 4918b5162e19SFrançois Tigeot if (crtc->funcs->set_property) 4919b5162e19SFrançois Tigeot ret = crtc->funcs->set_property(crtc, property, value); 4920b5162e19SFrançois Tigeot if (!ret) 4921b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 4922b5162e19SFrançois Tigeot 4923b5162e19SFrançois Tigeot return ret; 4924b5162e19SFrançois Tigeot } 4925b5162e19SFrançois Tigeot 49261b13d190SFrançois Tigeot /** 49271b13d190SFrançois Tigeot * drm_mode_plane_set_obj_prop - set the value of a property 49281b13d190SFrançois Tigeot * @plane: drm plane object to set property value for 49291b13d190SFrançois Tigeot * @property: property to set 49301b13d190SFrançois Tigeot * @value: value the property should be set to 49311b13d190SFrançois Tigeot * 49321b13d190SFrançois Tigeot * This functions sets a given property on a given plane object. This function 49331b13d190SFrançois Tigeot * calls the driver's ->set_property callback and changes the software state of 49341b13d190SFrançois Tigeot * the property if the callback succeeds. 49351b13d190SFrançois Tigeot * 49361b13d190SFrançois Tigeot * Returns: 49371b13d190SFrançois Tigeot * Zero on success, error code on failure. 49381b13d190SFrançois Tigeot */ 49391b13d190SFrançois Tigeot int drm_mode_plane_set_obj_prop(struct drm_plane *plane, 4940b5162e19SFrançois Tigeot struct drm_property *property, 4941b5162e19SFrançois Tigeot uint64_t value) 4942b5162e19SFrançois Tigeot { 4943b5162e19SFrançois Tigeot int ret = -EINVAL; 49441b13d190SFrançois Tigeot struct drm_mode_object *obj = &plane->base; 4945b5162e19SFrançois Tigeot 4946b5162e19SFrançois Tigeot if (plane->funcs->set_property) 4947b5162e19SFrançois Tigeot ret = plane->funcs->set_property(plane, property, value); 4948b5162e19SFrançois Tigeot if (!ret) 4949b5162e19SFrançois Tigeot drm_object_property_set_value(obj, property, value); 4950b5162e19SFrançois Tigeot 4951b5162e19SFrançois Tigeot return ret; 4952b5162e19SFrançois Tigeot } 49531b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_mode_plane_set_obj_prop); 4954b5162e19SFrançois Tigeot 4955ba55f2f5SFrançois Tigeot /** 49562c9916cdSFrançois Tigeot * drm_mode_obj_get_properties_ioctl - get the current value of a object's property 4957ba55f2f5SFrançois Tigeot * @dev: DRM device 4958ba55f2f5SFrançois Tigeot * @data: ioctl data 4959ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 4960ba55f2f5SFrançois Tigeot * 4961ba55f2f5SFrançois Tigeot * This function retrieves the current value for an object's property. Compared 4962ba55f2f5SFrançois Tigeot * to the connector specific ioctl this one is extended to also work on crtc and 4963ba55f2f5SFrançois Tigeot * plane objects. 4964ba55f2f5SFrançois Tigeot * 4965ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 4966ba55f2f5SFrançois Tigeot * 4967ba55f2f5SFrançois Tigeot * Returns: 49682c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 4969ba55f2f5SFrançois Tigeot */ 4970b5162e19SFrançois Tigeot int drm_mode_obj_get_properties_ioctl(struct drm_device *dev, void *data, 4971b5162e19SFrançois Tigeot struct drm_file *file_priv) 4972b5162e19SFrançois Tigeot { 4973b5162e19SFrançois Tigeot struct drm_mode_obj_get_properties *arg = data; 49745718399fSFrançois Tigeot struct drm_mode_object *obj; 4975b5162e19SFrançois Tigeot int ret = 0; 4976b5162e19SFrançois Tigeot 4977b5162e19SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 4978b5162e19SFrançois Tigeot return -EINVAL; 4979b5162e19SFrançois Tigeot 49804dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 4981b5162e19SFrançois Tigeot 4982b5162e19SFrançois Tigeot obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 4983b5162e19SFrançois Tigeot if (!obj) { 49849edbd4a0SFrançois Tigeot ret = -ENOENT; 4985b5162e19SFrançois Tigeot goto out; 4986b5162e19SFrançois Tigeot } 4987b5162e19SFrançois Tigeot if (!obj->properties) { 4988b5162e19SFrançois Tigeot ret = -EINVAL; 49898621f407SFrançois Tigeot goto out_unref; 4990b5162e19SFrançois Tigeot } 4991b5162e19SFrançois Tigeot 49922c9916cdSFrançois Tigeot ret = get_properties(obj, file_priv->atomic, 49932c9916cdSFrançois Tigeot (uint32_t __user *)(unsigned long)(arg->props_ptr), 49942c9916cdSFrançois Tigeot (uint64_t __user *)(unsigned long)(arg->prop_values_ptr), 49952c9916cdSFrançois Tigeot &arg->count_props); 4996b5162e19SFrançois Tigeot 49978621f407SFrançois Tigeot out_unref: 49988621f407SFrançois Tigeot drm_mode_object_unreference(obj); 4999b5162e19SFrançois Tigeot out: 50004dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 5001b5162e19SFrançois Tigeot return ret; 5002b5162e19SFrançois Tigeot } 5003b5162e19SFrançois Tigeot 5004ba55f2f5SFrançois Tigeot /** 5005ba55f2f5SFrançois Tigeot * drm_mode_obj_set_property_ioctl - set the current value of an object's property 5006ba55f2f5SFrançois Tigeot * @dev: DRM device 5007ba55f2f5SFrançois Tigeot * @data: ioctl data 5008ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5009ba55f2f5SFrançois Tigeot * 5010ba55f2f5SFrançois Tigeot * This function sets the current value for an object's property. It also calls 5011ba55f2f5SFrançois Tigeot * into a driver's ->set_property callback to update the hardware state. 5012ba55f2f5SFrançois Tigeot * Compared to the connector specific ioctl this one is extended to also work on 5013ba55f2f5SFrançois Tigeot * crtc and plane objects. 5014ba55f2f5SFrançois Tigeot * 5015ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5016ba55f2f5SFrançois Tigeot * 5017ba55f2f5SFrançois Tigeot * Returns: 50182c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5019ba55f2f5SFrançois Tigeot */ 5020b5162e19SFrançois Tigeot int drm_mode_obj_set_property_ioctl(struct drm_device *dev, void *data, 5021b5162e19SFrançois Tigeot struct drm_file *file_priv) 5022b5162e19SFrançois Tigeot { 5023b5162e19SFrançois Tigeot struct drm_mode_obj_set_property *arg = data; 5024b5162e19SFrançois Tigeot struct drm_mode_object *arg_obj; 5025b5162e19SFrançois Tigeot struct drm_mode_object *prop_obj; 50265718399fSFrançois Tigeot struct drm_property *property; 50272c9916cdSFrançois Tigeot int i, ret = -EINVAL; 50282c9916cdSFrançois Tigeot struct drm_mode_object *ref; 50295718399fSFrançois Tigeot 50305718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 50315718399fSFrançois Tigeot return -EINVAL; 50325718399fSFrançois Tigeot 50334dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 50345718399fSFrançois Tigeot 5035b5162e19SFrançois Tigeot arg_obj = drm_mode_object_find(dev, arg->obj_id, arg->obj_type); 50369edbd4a0SFrançois Tigeot if (!arg_obj) { 50379edbd4a0SFrançois Tigeot ret = -ENOENT; 50385718399fSFrançois Tigeot goto out; 50399edbd4a0SFrançois Tigeot } 5040b5162e19SFrançois Tigeot if (!arg_obj->properties) 50418621f407SFrançois Tigeot goto out_unref; 50425718399fSFrançois Tigeot 5043b5162e19SFrançois Tigeot for (i = 0; i < arg_obj->properties->count; i++) 50442c9916cdSFrançois Tigeot if (arg_obj->properties->properties[i]->base.id == arg->prop_id) 5045b5162e19SFrançois Tigeot break; 5046b5162e19SFrançois Tigeot 5047b5162e19SFrançois Tigeot if (i == arg_obj->properties->count) 50488621f407SFrançois Tigeot goto out_unref; 5049b5162e19SFrançois Tigeot 5050b5162e19SFrançois Tigeot prop_obj = drm_mode_object_find(dev, arg->prop_id, 5051b5162e19SFrançois Tigeot DRM_MODE_OBJECT_PROPERTY); 50529edbd4a0SFrançois Tigeot if (!prop_obj) { 50539edbd4a0SFrançois Tigeot ret = -ENOENT; 50548621f407SFrançois Tigeot goto out_unref; 50559edbd4a0SFrançois Tigeot } 5056b5162e19SFrançois Tigeot property = obj_to_property(prop_obj); 5057b5162e19SFrançois Tigeot 50582c9916cdSFrançois Tigeot if (!drm_property_change_valid_get(property, arg->value, &ref)) 50598621f407SFrançois Tigeot goto out_unref; 5060b5162e19SFrançois Tigeot 5061b5162e19SFrançois Tigeot switch (arg_obj->type) { 5062b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CONNECTOR: 5063b5162e19SFrançois Tigeot ret = drm_mode_connector_set_obj_prop(arg_obj, property, 5064b5162e19SFrançois Tigeot arg->value); 5065b5162e19SFrançois Tigeot break; 5066b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_CRTC: 5067b5162e19SFrançois Tigeot ret = drm_mode_crtc_set_obj_prop(arg_obj, property, arg->value); 5068b5162e19SFrançois Tigeot break; 5069b5162e19SFrançois Tigeot case DRM_MODE_OBJECT_PLANE: 50701b13d190SFrançois Tigeot ret = drm_mode_plane_set_obj_prop(obj_to_plane(arg_obj), 50711b13d190SFrançois Tigeot property, arg->value); 50725718399fSFrançois Tigeot break; 50735718399fSFrançois Tigeot } 50745718399fSFrançois Tigeot 50752c9916cdSFrançois Tigeot drm_property_change_valid_put(property, ref); 50762c9916cdSFrançois Tigeot 50778621f407SFrançois Tigeot out_unref: 50788621f407SFrançois Tigeot drm_mode_object_unreference(arg_obj); 50795718399fSFrançois Tigeot out: 50804dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 50815718399fSFrançois Tigeot return ret; 50825718399fSFrançois Tigeot } 50835718399fSFrançois Tigeot 5084ba55f2f5SFrançois Tigeot /** 5085ba55f2f5SFrançois Tigeot * drm_mode_connector_attach_encoder - attach a connector to an encoder 5086ba55f2f5SFrançois Tigeot * @connector: connector to attach 5087ba55f2f5SFrançois Tigeot * @encoder: encoder to attach @connector to 5088ba55f2f5SFrançois Tigeot * 5089ba55f2f5SFrançois Tigeot * This function links up a connector to an encoder. Note that the routing 5090ba55f2f5SFrançois Tigeot * restrictions between encoders and crtcs are exposed to userspace through the 5091ba55f2f5SFrançois Tigeot * possible_clones and possible_crtcs bitmasks. 5092ba55f2f5SFrançois Tigeot * 5093ba55f2f5SFrançois Tigeot * Returns: 50942c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5095ba55f2f5SFrançois Tigeot */ 50965718399fSFrançois Tigeot int drm_mode_connector_attach_encoder(struct drm_connector *connector, 50975718399fSFrançois Tigeot struct drm_encoder *encoder) 50985718399fSFrançois Tigeot { 50995718399fSFrançois Tigeot int i; 51005718399fSFrançois Tigeot 5101aee94f86SFrançois Tigeot /* 5102aee94f86SFrançois Tigeot * In the past, drivers have attempted to model the static association 5103aee94f86SFrançois Tigeot * of connector to encoder in simple connector/encoder devices using a 5104aee94f86SFrançois Tigeot * direct assignment of connector->encoder = encoder. This connection 5105aee94f86SFrançois Tigeot * is a logical one and the responsibility of the core, so drivers are 5106aee94f86SFrançois Tigeot * expected not to mess with this. 5107aee94f86SFrançois Tigeot * 5108aee94f86SFrançois Tigeot * Note that the error return should've been enough here, but a large 5109aee94f86SFrançois Tigeot * majority of drivers ignores the return value, so add in a big WARN 5110aee94f86SFrançois Tigeot * to get people's attention. 5111aee94f86SFrançois Tigeot */ 5112aee94f86SFrançois Tigeot if (WARN_ON(connector->encoder)) 5113aee94f86SFrançois Tigeot return -EINVAL; 5114aee94f86SFrançois Tigeot 51155718399fSFrançois Tigeot for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) { 51165718399fSFrançois Tigeot if (connector->encoder_ids[i] == 0) { 51175718399fSFrançois Tigeot connector->encoder_ids[i] = encoder->base.id; 51185718399fSFrançois Tigeot return 0; 51195718399fSFrançois Tigeot } 51205718399fSFrançois Tigeot } 51215718399fSFrançois Tigeot return -ENOMEM; 51225718399fSFrançois Tigeot } 5123b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_connector_attach_encoder); 51245718399fSFrançois Tigeot 5125ba55f2f5SFrançois Tigeot /** 5126ba55f2f5SFrançois Tigeot * drm_mode_crtc_set_gamma_size - set the gamma table size 5127ba55f2f5SFrançois Tigeot * @crtc: CRTC to set the gamma table size for 5128ba55f2f5SFrançois Tigeot * @gamma_size: size of the gamma table 5129ba55f2f5SFrançois Tigeot * 5130ba55f2f5SFrançois Tigeot * Drivers which support gamma tables should set this to the supported gamma 5131ba55f2f5SFrançois Tigeot * table size when initializing the CRTC. Currently the drm core only supports a 5132ba55f2f5SFrançois Tigeot * fixed gamma table size. 5133ba55f2f5SFrançois Tigeot * 5134ba55f2f5SFrançois Tigeot * Returns: 51352c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5136ba55f2f5SFrançois Tigeot */ 51375718399fSFrançois Tigeot int drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc, 51385718399fSFrançois Tigeot int gamma_size) 51395718399fSFrançois Tigeot { 51405718399fSFrançois Tigeot crtc->gamma_size = gamma_size; 51415718399fSFrançois Tigeot 51422c9916cdSFrançois Tigeot crtc->gamma_store = kcalloc(gamma_size, sizeof(uint16_t) * 3, 51432c9916cdSFrançois Tigeot GFP_KERNEL); 5144b5162e19SFrançois Tigeot if (!crtc->gamma_store) { 5145b5162e19SFrançois Tigeot crtc->gamma_size = 0; 5146b5162e19SFrançois Tigeot return -ENOMEM; 5147b5162e19SFrançois Tigeot } 51485718399fSFrançois Tigeot 51495718399fSFrançois Tigeot return 0; 51505718399fSFrançois Tigeot } 5151b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_crtc_set_gamma_size); 51525718399fSFrançois Tigeot 5153ba55f2f5SFrançois Tigeot /** 5154ba55f2f5SFrançois Tigeot * drm_mode_gamma_set_ioctl - set the gamma table 5155ba55f2f5SFrançois Tigeot * @dev: DRM device 5156ba55f2f5SFrançois Tigeot * @data: ioctl data 5157ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5158ba55f2f5SFrançois Tigeot * 5159ba55f2f5SFrançois Tigeot * Set the gamma table of a CRTC to the one passed in by the user. Userspace can 5160ba55f2f5SFrançois Tigeot * inquire the required gamma table size through drm_mode_gamma_get_ioctl. 5161ba55f2f5SFrançois Tigeot * 5162ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5163ba55f2f5SFrançois Tigeot * 5164ba55f2f5SFrançois Tigeot * Returns: 51652c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5166ba55f2f5SFrançois Tigeot */ 51675718399fSFrançois Tigeot int drm_mode_gamma_set_ioctl(struct drm_device *dev, 51685718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 51695718399fSFrançois Tigeot { 51705718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 51715718399fSFrançois Tigeot struct drm_crtc *crtc; 51725718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 51735718399fSFrançois Tigeot int size; 51745718399fSFrançois Tigeot int ret = 0; 51755718399fSFrançois Tigeot 51765718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 51775718399fSFrançois Tigeot return -EINVAL; 51785718399fSFrançois Tigeot 51794dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 5180ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_lut->crtc_id); 5181ba55f2f5SFrançois Tigeot if (!crtc) { 51829edbd4a0SFrançois Tigeot ret = -ENOENT; 51835718399fSFrançois Tigeot goto out; 51845718399fSFrançois Tigeot } 51855718399fSFrançois Tigeot 5186b5162e19SFrançois Tigeot if (crtc->funcs->gamma_set == NULL) { 5187b5162e19SFrançois Tigeot ret = -ENOSYS; 5188b5162e19SFrançois Tigeot goto out; 5189b5162e19SFrançois Tigeot } 5190b5162e19SFrançois Tigeot 51915718399fSFrançois Tigeot /* memcpy into gamma store */ 51925718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 51935718399fSFrançois Tigeot ret = -EINVAL; 51945718399fSFrançois Tigeot goto out; 51955718399fSFrançois Tigeot } 51965718399fSFrançois Tigeot 51975718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 51985718399fSFrançois Tigeot r_base = crtc->gamma_store; 5199b5162e19SFrançois Tigeot if (copy_from_user(r_base, (void __user *)(unsigned long)crtc_lut->red, size)) { 52005718399fSFrançois Tigeot ret = -EFAULT; 52015718399fSFrançois Tigeot goto out; 52025718399fSFrançois Tigeot } 52035718399fSFrançois Tigeot 52045718399fSFrançois Tigeot g_base = (char *)r_base + size; 5205b5162e19SFrançois Tigeot if (copy_from_user(g_base, (void __user *)(unsigned long)crtc_lut->green, size)) { 52065718399fSFrançois Tigeot ret = -EFAULT; 52075718399fSFrançois Tigeot goto out; 52085718399fSFrançois Tigeot } 52095718399fSFrançois Tigeot 521006fede5aSFrançois Tigeot b_base = (char *)g_base + size; 5211b5162e19SFrançois Tigeot if (copy_from_user(b_base, (void __user *)(unsigned long)crtc_lut->blue, size)) { 52125718399fSFrançois Tigeot ret = -EFAULT; 52135718399fSFrançois Tigeot goto out; 52145718399fSFrançois Tigeot } 52155718399fSFrançois Tigeot 52165718399fSFrançois Tigeot crtc->funcs->gamma_set(crtc, r_base, g_base, b_base, 0, crtc->gamma_size); 52175718399fSFrançois Tigeot 52185718399fSFrançois Tigeot out: 52194dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 52205718399fSFrançois Tigeot return ret; 52215718399fSFrançois Tigeot 52225718399fSFrançois Tigeot } 52235718399fSFrançois Tigeot 5224ba55f2f5SFrançois Tigeot /** 5225ba55f2f5SFrançois Tigeot * drm_mode_gamma_get_ioctl - get the gamma table 5226ba55f2f5SFrançois Tigeot * @dev: DRM device 5227ba55f2f5SFrançois Tigeot * @data: ioctl data 5228ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5229ba55f2f5SFrançois Tigeot * 5230ba55f2f5SFrançois Tigeot * Copy the current gamma table into the storage provided. This also provides 5231ba55f2f5SFrançois Tigeot * the gamma table size the driver expects, which can be used to size the 5232ba55f2f5SFrançois Tigeot * allocated storage. 5233ba55f2f5SFrançois Tigeot * 5234ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5235ba55f2f5SFrançois Tigeot * 5236ba55f2f5SFrançois Tigeot * Returns: 52372c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5238ba55f2f5SFrançois Tigeot */ 52395718399fSFrançois Tigeot int drm_mode_gamma_get_ioctl(struct drm_device *dev, 52405718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 52415718399fSFrançois Tigeot { 52425718399fSFrançois Tigeot struct drm_mode_crtc_lut *crtc_lut = data; 52435718399fSFrançois Tigeot struct drm_crtc *crtc; 52445718399fSFrançois Tigeot void *r_base, *g_base, *b_base; 52455718399fSFrançois Tigeot int size; 52465718399fSFrançois Tigeot int ret = 0; 52475718399fSFrançois Tigeot 52485718399fSFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 52495718399fSFrançois Tigeot return -EINVAL; 52505718399fSFrançois Tigeot 52514dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 5252ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, crtc_lut->crtc_id); 5253ba55f2f5SFrançois Tigeot if (!crtc) { 52549edbd4a0SFrançois Tigeot ret = -ENOENT; 52555718399fSFrançois Tigeot goto out; 52565718399fSFrançois Tigeot } 52575718399fSFrançois Tigeot 52585718399fSFrançois Tigeot /* memcpy into gamma store */ 52595718399fSFrançois Tigeot if (crtc_lut->gamma_size != crtc->gamma_size) { 52605718399fSFrançois Tigeot ret = -EINVAL; 52615718399fSFrançois Tigeot goto out; 52625718399fSFrançois Tigeot } 52635718399fSFrançois Tigeot 52645718399fSFrançois Tigeot size = crtc_lut->gamma_size * (sizeof(uint16_t)); 52655718399fSFrançois Tigeot r_base = crtc->gamma_store; 5266b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->red, r_base, size)) { 52675718399fSFrançois Tigeot ret = -EFAULT; 52685718399fSFrançois Tigeot goto out; 52695718399fSFrançois Tigeot } 52705718399fSFrançois Tigeot 52715718399fSFrançois Tigeot g_base = (char *)r_base + size; 5272b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->green, g_base, size)) { 52735718399fSFrançois Tigeot ret = -EFAULT; 52745718399fSFrançois Tigeot goto out; 52755718399fSFrançois Tigeot } 52765718399fSFrançois Tigeot 52775718399fSFrançois Tigeot b_base = (char *)g_base + size; 5278b5162e19SFrançois Tigeot if (copy_to_user((void __user *)(unsigned long)crtc_lut->blue, b_base, size)) { 52795718399fSFrançois Tigeot ret = -EFAULT; 52805718399fSFrançois Tigeot goto out; 52815718399fSFrançois Tigeot } 52825718399fSFrançois Tigeot out: 52834dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 52845718399fSFrançois Tigeot return ret; 52855718399fSFrançois Tigeot } 52865718399fSFrançois Tigeot 5287ba55f2f5SFrançois Tigeot /** 5288ba55f2f5SFrançois Tigeot * drm_mode_page_flip_ioctl - schedule an asynchronous fb update 5289ba55f2f5SFrançois Tigeot * @dev: DRM device 5290ba55f2f5SFrançois Tigeot * @data: ioctl data 5291ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5292ba55f2f5SFrançois Tigeot * 5293ba55f2f5SFrançois Tigeot * This schedules an asynchronous update on a given CRTC, called page flip. 5294ba55f2f5SFrançois Tigeot * Optionally a drm event is generated to signal the completion of the event. 5295ba55f2f5SFrançois Tigeot * Generic drivers cannot assume that a pageflip with changed framebuffer 5296ba55f2f5SFrançois Tigeot * properties (including driver specific metadata like tiling layout) will work, 5297ba55f2f5SFrançois Tigeot * but some drivers support e.g. pixel format changes through the pageflip 5298ba55f2f5SFrançois Tigeot * ioctl. 5299ba55f2f5SFrançois Tigeot * 5300ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5301ba55f2f5SFrançois Tigeot * 5302ba55f2f5SFrançois Tigeot * Returns: 53032c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5304a5f72378SFrançois Tigeot */ 5305b5162e19SFrançois Tigeot int drm_mode_page_flip_ioctl(struct drm_device *dev, 5306b5162e19SFrançois Tigeot void *data, struct drm_file *file_priv) 53075718399fSFrançois Tigeot { 53085718399fSFrançois Tigeot struct drm_mode_crtc_page_flip *page_flip = data; 53095718399fSFrançois Tigeot struct drm_crtc *crtc; 53101b13d190SFrançois Tigeot struct drm_framebuffer *fb = NULL; 53115718399fSFrançois Tigeot struct drm_pending_vblank_event *e = NULL; 5312b5162e19SFrançois Tigeot int ret = -EINVAL; 53135718399fSFrançois Tigeot 53148621f407SFrançois Tigeot if (!drm_core_check_feature(dev, DRIVER_MODESET)) 53158621f407SFrançois Tigeot return -EINVAL; 53168621f407SFrançois Tigeot 53175718399fSFrançois Tigeot if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS || 53185718399fSFrançois Tigeot page_flip->reserved != 0) 5319b5162e19SFrançois Tigeot return -EINVAL; 53205718399fSFrançois Tigeot 53219edbd4a0SFrançois Tigeot if ((page_flip->flags & DRM_MODE_PAGE_FLIP_ASYNC) && !dev->mode_config.async_page_flip) 53229edbd4a0SFrançois Tigeot return -EINVAL; 53239edbd4a0SFrançois Tigeot 5324ba55f2f5SFrançois Tigeot crtc = drm_crtc_find(dev, page_flip->crtc_id); 5325ba55f2f5SFrançois Tigeot if (!crtc) 53269edbd4a0SFrançois Tigeot return -ENOENT; 53275718399fSFrançois Tigeot 53282c9916cdSFrançois Tigeot drm_modeset_lock_crtc(crtc, crtc->primary); 5329ba55f2f5SFrançois Tigeot if (crtc->primary->fb == NULL) { 53305718399fSFrançois Tigeot /* The framebuffer is currently unbound, presumably 53315718399fSFrançois Tigeot * due to a hotplug event, that userspace has not 53325718399fSFrançois Tigeot * yet discovered. 53335718399fSFrançois Tigeot */ 5334b5162e19SFrançois Tigeot ret = -EBUSY; 53355718399fSFrançois Tigeot goto out; 53365718399fSFrançois Tigeot } 53375718399fSFrançois Tigeot 53385718399fSFrançois Tigeot if (crtc->funcs->page_flip == NULL) 53395718399fSFrançois Tigeot goto out; 53405718399fSFrançois Tigeot 53414dbb207bSFrançois Tigeot fb = drm_framebuffer_lookup(dev, page_flip->fb_id); 53429edbd4a0SFrançois Tigeot if (!fb) { 53439edbd4a0SFrançois Tigeot ret = -ENOENT; 53445718399fSFrançois Tigeot goto out; 53455718399fSFrançois Tigeot } 53465718399fSFrançois Tigeot 5347352ff8bdSFrançois Tigeot if (crtc->state) { 5348352ff8bdSFrançois Tigeot const struct drm_plane_state *state = crtc->primary->state; 5349352ff8bdSFrançois Tigeot 5350352ff8bdSFrançois Tigeot ret = check_src_coords(state->src_x, state->src_y, 5351352ff8bdSFrançois Tigeot state->src_w, state->src_h, fb); 5352352ff8bdSFrançois Tigeot } else { 53539edbd4a0SFrançois Tigeot ret = drm_crtc_check_viewport(crtc, crtc->x, crtc->y, &crtc->mode, fb); 5354352ff8bdSFrançois Tigeot } 53559edbd4a0SFrançois Tigeot if (ret) 53569edbd4a0SFrançois Tigeot goto out; 53579edbd4a0SFrançois Tigeot 5358ba55f2f5SFrançois Tigeot if (crtc->primary->fb->pixel_format != fb->pixel_format) { 53594dbb207bSFrançois Tigeot DRM_DEBUG_KMS("Page flip is not allowed to change frame buffer format.\n"); 53604dbb207bSFrançois Tigeot ret = -EINVAL; 53614dbb207bSFrançois Tigeot goto out; 53624dbb207bSFrançois Tigeot } 53634dbb207bSFrançois Tigeot 53645718399fSFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) { 5365c0e85e96SFrançois Tigeot e = kzalloc(sizeof *e, GFP_KERNEL); 5366c0e85e96SFrançois Tigeot if (!e) { 5367b5162e19SFrançois Tigeot ret = -ENOMEM; 53685718399fSFrançois Tigeot goto out; 53695718399fSFrançois Tigeot } 53705718399fSFrançois Tigeot e->event.base.type = DRM_EVENT_FLIP_COMPLETE; 53712c9916cdSFrançois Tigeot e->event.base.length = sizeof(e->event); 53725718399fSFrançois Tigeot e->event.user_data = page_flip->user_data; 5373c0e85e96SFrançois Tigeot ret = drm_event_reserve_init(dev, file_priv, &e->base, &e->event.base); 5374c0e85e96SFrançois Tigeot if (ret) { 5375c0e85e96SFrançois Tigeot kfree(e); 5376c0e85e96SFrançois Tigeot goto out; 5377c0e85e96SFrançois Tigeot } 53785718399fSFrançois Tigeot } 53795718399fSFrançois Tigeot 53801b13d190SFrançois Tigeot crtc->primary->old_fb = crtc->primary->fb; 53819edbd4a0SFrançois Tigeot ret = crtc->funcs->page_flip(crtc, fb, e, page_flip->flags); 5382b5162e19SFrançois Tigeot if (ret) { 5383c0e85e96SFrançois Tigeot if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) 5384c0e85e96SFrançois Tigeot drm_event_cancel_free(dev, &e->base); 53854dbb207bSFrançois Tigeot /* Keep the old fb, don't unref it. */ 53861b13d190SFrançois Tigeot crtc->primary->old_fb = NULL; 53874dbb207bSFrançois Tigeot } else { 5388a05eeebfSFrançois Tigeot crtc->primary->fb = fb; 53894dbb207bSFrançois Tigeot /* Unref only the old framebuffer. */ 53904dbb207bSFrançois Tigeot fb = NULL; 53915718399fSFrançois Tigeot } 53925718399fSFrançois Tigeot 53935718399fSFrançois Tigeot out: 53944dbb207bSFrançois Tigeot if (fb) 53954dbb207bSFrançois Tigeot drm_framebuffer_unreference(fb); 53961b13d190SFrançois Tigeot if (crtc->primary->old_fb) 53971b13d190SFrançois Tigeot drm_framebuffer_unreference(crtc->primary->old_fb); 53981b13d190SFrançois Tigeot crtc->primary->old_fb = NULL; 53991b13d190SFrançois Tigeot drm_modeset_unlock_crtc(crtc); 54004dbb207bSFrançois Tigeot 5401b5162e19SFrançois Tigeot return ret; 54025718399fSFrançois Tigeot } 54035718399fSFrançois Tigeot 5404ba55f2f5SFrançois Tigeot /** 5405ba55f2f5SFrançois Tigeot * drm_mode_config_reset - call ->reset callbacks 5406ba55f2f5SFrançois Tigeot * @dev: drm device 5407ba55f2f5SFrançois Tigeot * 5408ba55f2f5SFrançois Tigeot * This functions calls all the crtc's, encoder's and connector's ->reset 5409ba55f2f5SFrançois Tigeot * callback. Drivers can use this in e.g. their driver load or resume code to 5410ba55f2f5SFrançois Tigeot * reset hardware and software state. 5411ba55f2f5SFrançois Tigeot */ 54125718399fSFrançois Tigeot void drm_mode_config_reset(struct drm_device *dev) 54135718399fSFrançois Tigeot { 54145718399fSFrançois Tigeot struct drm_crtc *crtc; 54151b13d190SFrançois Tigeot struct drm_plane *plane; 54165718399fSFrançois Tigeot struct drm_encoder *encoder; 54175718399fSFrançois Tigeot struct drm_connector *connector; 54185718399fSFrançois Tigeot 5419a05eeebfSFrançois Tigeot drm_for_each_plane(plane, dev) 54201b13d190SFrançois Tigeot if (plane->funcs->reset) 54211b13d190SFrançois Tigeot plane->funcs->reset(plane); 54221b13d190SFrançois Tigeot 5423a05eeebfSFrançois Tigeot drm_for_each_crtc(crtc, dev) 54245718399fSFrançois Tigeot if (crtc->funcs->reset) 54255718399fSFrançois Tigeot crtc->funcs->reset(crtc); 54265718399fSFrançois Tigeot 5427a05eeebfSFrançois Tigeot drm_for_each_encoder(encoder, dev) 54285718399fSFrançois Tigeot if (encoder->funcs->reset) 54295718399fSFrançois Tigeot encoder->funcs->reset(encoder); 54305718399fSFrançois Tigeot 5431a05eeebfSFrançois Tigeot mutex_lock(&dev->mode_config.mutex); 5432917ec290SFrançois Tigeot drm_for_each_connector(connector, dev) 54335718399fSFrançois Tigeot if (connector->funcs->reset) 54345718399fSFrançois Tigeot connector->funcs->reset(connector); 5435a05eeebfSFrançois Tigeot mutex_unlock(&dev->mode_config.mutex); 5436a05eeebfSFrançois Tigeot } 5437b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_reset); 54385718399fSFrançois Tigeot 5439ba55f2f5SFrançois Tigeot /** 5440ba55f2f5SFrançois Tigeot * drm_mode_create_dumb_ioctl - create a dumb backing storage buffer 5441ba55f2f5SFrançois Tigeot * @dev: DRM device 5442ba55f2f5SFrançois Tigeot * @data: ioctl data 5443ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5444ba55f2f5SFrançois Tigeot * 5445ba55f2f5SFrançois Tigeot * This creates a new dumb buffer in the driver's backing storage manager (GEM, 5446ba55f2f5SFrançois Tigeot * TTM or something else entirely) and returns the resulting buffer handle. This 5447ba55f2f5SFrançois Tigeot * handle can then be wrapped up into a framebuffer modeset object. 5448ba55f2f5SFrançois Tigeot * 5449ba55f2f5SFrançois Tigeot * Note that userspace is not allowed to use such objects for render 5450ba55f2f5SFrançois Tigeot * acceleration - drivers must create their own private ioctls for such a use 5451ba55f2f5SFrançois Tigeot * case. 5452ba55f2f5SFrançois Tigeot * 5453ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5454ba55f2f5SFrançois Tigeot * 5455ba55f2f5SFrançois Tigeot * Returns: 54562c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5457ba55f2f5SFrançois Tigeot */ 54585718399fSFrançois Tigeot int drm_mode_create_dumb_ioctl(struct drm_device *dev, 54595718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 54605718399fSFrançois Tigeot { 54615718399fSFrançois Tigeot struct drm_mode_create_dumb *args = data; 5462ba55f2f5SFrançois Tigeot u32 cpp, stride, size; 54635718399fSFrançois Tigeot 54645718399fSFrançois Tigeot if (!dev->driver->dumb_create) 5465b5162e19SFrançois Tigeot return -ENOSYS; 5466ba55f2f5SFrançois Tigeot if (!args->width || !args->height || !args->bpp) 5467ba55f2f5SFrançois Tigeot return -EINVAL; 5468ba55f2f5SFrançois Tigeot 5469ba55f2f5SFrançois Tigeot /* overflow checks for 32bit size calculations */ 547024edb884SFrançois Tigeot /* NOTE: DIV_ROUND_UP() can overflow */ 5471ba55f2f5SFrançois Tigeot cpp = DIV_ROUND_UP(args->bpp, 8); 547224edb884SFrançois Tigeot if (!cpp || cpp > 0xffffffffU / args->width) 5473ba55f2f5SFrançois Tigeot return -EINVAL; 5474ba55f2f5SFrançois Tigeot stride = cpp * args->width; 5475ba55f2f5SFrançois Tigeot if (args->height > 0xffffffffU / stride) 5476ba55f2f5SFrançois Tigeot return -EINVAL; 5477ba55f2f5SFrançois Tigeot 5478ba55f2f5SFrançois Tigeot /* test for wrap-around */ 5479ba55f2f5SFrançois Tigeot size = args->height * stride; 5480ba55f2f5SFrançois Tigeot if (PAGE_ALIGN(size) == 0) 5481ba55f2f5SFrançois Tigeot return -EINVAL; 5482ba55f2f5SFrançois Tigeot 54832c9916cdSFrançois Tigeot /* 54842c9916cdSFrançois Tigeot * handle, pitch and size are output parameters. Zero them out to 54852c9916cdSFrançois Tigeot * prevent drivers from accidentally using uninitialized data. Since 54862c9916cdSFrançois Tigeot * not all existing userspace is clearing these fields properly we 54872c9916cdSFrançois Tigeot * cannot reject IOCTL with garbage in them. 54882c9916cdSFrançois Tigeot */ 54892c9916cdSFrançois Tigeot args->handle = 0; 54902c9916cdSFrançois Tigeot args->pitch = 0; 54912c9916cdSFrançois Tigeot args->size = 0; 54922c9916cdSFrançois Tigeot 54935718399fSFrançois Tigeot return dev->driver->dumb_create(file_priv, dev, args); 54945718399fSFrançois Tigeot } 54955718399fSFrançois Tigeot 5496ba55f2f5SFrançois Tigeot /** 5497ba55f2f5SFrançois Tigeot * drm_mode_mmap_dumb_ioctl - create an mmap offset for a dumb backing storage buffer 5498ba55f2f5SFrançois Tigeot * @dev: DRM device 5499ba55f2f5SFrançois Tigeot * @data: ioctl data 5500ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5501ba55f2f5SFrançois Tigeot * 5502ba55f2f5SFrançois Tigeot * Allocate an offset in the drm device node's address space to be able to 5503ba55f2f5SFrançois Tigeot * memory map a dumb buffer. 5504ba55f2f5SFrançois Tigeot * 5505ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5506ba55f2f5SFrançois Tigeot * 5507ba55f2f5SFrançois Tigeot * Returns: 55082c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5509ba55f2f5SFrançois Tigeot */ 55105718399fSFrançois Tigeot int drm_mode_mmap_dumb_ioctl(struct drm_device *dev, 55115718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 55125718399fSFrançois Tigeot { 55135718399fSFrançois Tigeot struct drm_mode_map_dumb *args = data; 55145718399fSFrançois Tigeot 55155718399fSFrançois Tigeot /* call driver ioctl to get mmap offset */ 55165718399fSFrançois Tigeot if (!dev->driver->dumb_map_offset) 5517b5162e19SFrançois Tigeot return -ENOSYS; 55185718399fSFrançois Tigeot 5519f77dbd6cSFrançois Tigeot return dev->driver->dumb_map_offset(file_priv, dev, args->handle, (uint64_t *)&args->offset); 55205718399fSFrançois Tigeot } 55215718399fSFrançois Tigeot 5522ba55f2f5SFrançois Tigeot /** 5523ba55f2f5SFrançois Tigeot * drm_mode_destroy_dumb_ioctl - destroy a dumb backing strage buffer 5524ba55f2f5SFrançois Tigeot * @dev: DRM device 5525ba55f2f5SFrançois Tigeot * @data: ioctl data 5526ba55f2f5SFrançois Tigeot * @file_priv: DRM file info 5527ba55f2f5SFrançois Tigeot * 5528ba55f2f5SFrançois Tigeot * This destroys the userspace handle for the given dumb backing storage buffer. 5529ba55f2f5SFrançois Tigeot * Since buffer objects must be reference counted in the kernel a buffer object 5530ba55f2f5SFrançois Tigeot * won't be immediately freed if a framebuffer modeset object still uses it. 5531ba55f2f5SFrançois Tigeot * 5532ba55f2f5SFrançois Tigeot * Called by the user via ioctl. 5533ba55f2f5SFrançois Tigeot * 5534ba55f2f5SFrançois Tigeot * Returns: 55352c9916cdSFrançois Tigeot * Zero on success, negative errno on failure. 5536ba55f2f5SFrançois Tigeot */ 55375718399fSFrançois Tigeot int drm_mode_destroy_dumb_ioctl(struct drm_device *dev, 55385718399fSFrançois Tigeot void *data, struct drm_file *file_priv) 55395718399fSFrançois Tigeot { 55405718399fSFrançois Tigeot struct drm_mode_destroy_dumb *args = data; 55415718399fSFrançois Tigeot 55425718399fSFrançois Tigeot if (!dev->driver->dumb_destroy) 5543b5162e19SFrançois Tigeot return -ENOSYS; 55445718399fSFrançois Tigeot 55455718399fSFrançois Tigeot return dev->driver->dumb_destroy(file_priv, dev, args->handle); 55465718399fSFrançois Tigeot } 55475718399fSFrançois Tigeot 5548ba55f2f5SFrançois Tigeot /** 5549ba55f2f5SFrançois Tigeot * drm_fb_get_bpp_depth - get the bpp/depth values for format 5550ba55f2f5SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 5551ba55f2f5SFrançois Tigeot * @depth: storage for the depth value 5552ba55f2f5SFrançois Tigeot * @bpp: storage for the bpp value 5553ba55f2f5SFrançois Tigeot * 5554ba55f2f5SFrançois Tigeot * This only supports RGB formats here for compat with code that doesn't use 5555ba55f2f5SFrançois Tigeot * pixel formats directly yet. 55565718399fSFrançois Tigeot */ 55575718399fSFrançois Tigeot void drm_fb_get_bpp_depth(uint32_t format, unsigned int *depth, 55585718399fSFrançois Tigeot int *bpp) 55595718399fSFrançois Tigeot { 55605718399fSFrançois Tigeot switch (format) { 55614dbb207bSFrançois Tigeot case DRM_FORMAT_C8: 55625718399fSFrançois Tigeot case DRM_FORMAT_RGB332: 55635718399fSFrançois Tigeot case DRM_FORMAT_BGR233: 55645718399fSFrançois Tigeot *depth = 8; 55655718399fSFrançois Tigeot *bpp = 8; 55665718399fSFrançois Tigeot break; 55675718399fSFrançois Tigeot case DRM_FORMAT_XRGB1555: 55685718399fSFrançois Tigeot case DRM_FORMAT_XBGR1555: 55695718399fSFrançois Tigeot case DRM_FORMAT_RGBX5551: 55705718399fSFrançois Tigeot case DRM_FORMAT_BGRX5551: 55715718399fSFrançois Tigeot case DRM_FORMAT_ARGB1555: 55725718399fSFrançois Tigeot case DRM_FORMAT_ABGR1555: 55735718399fSFrançois Tigeot case DRM_FORMAT_RGBA5551: 55745718399fSFrançois Tigeot case DRM_FORMAT_BGRA5551: 55755718399fSFrançois Tigeot *depth = 15; 55765718399fSFrançois Tigeot *bpp = 16; 55775718399fSFrançois Tigeot break; 55785718399fSFrançois Tigeot case DRM_FORMAT_RGB565: 55795718399fSFrançois Tigeot case DRM_FORMAT_BGR565: 55805718399fSFrançois Tigeot *depth = 16; 55815718399fSFrançois Tigeot *bpp = 16; 55825718399fSFrançois Tigeot break; 55835718399fSFrançois Tigeot case DRM_FORMAT_RGB888: 55845718399fSFrançois Tigeot case DRM_FORMAT_BGR888: 55855718399fSFrançois Tigeot *depth = 24; 55865718399fSFrançois Tigeot *bpp = 24; 55875718399fSFrançois Tigeot break; 55885718399fSFrançois Tigeot case DRM_FORMAT_XRGB8888: 55895718399fSFrançois Tigeot case DRM_FORMAT_XBGR8888: 55905718399fSFrançois Tigeot case DRM_FORMAT_RGBX8888: 55915718399fSFrançois Tigeot case DRM_FORMAT_BGRX8888: 55925718399fSFrançois Tigeot *depth = 24; 55935718399fSFrançois Tigeot *bpp = 32; 55945718399fSFrançois Tigeot break; 55955718399fSFrançois Tigeot case DRM_FORMAT_XRGB2101010: 55965718399fSFrançois Tigeot case DRM_FORMAT_XBGR2101010: 55975718399fSFrançois Tigeot case DRM_FORMAT_RGBX1010102: 55985718399fSFrançois Tigeot case DRM_FORMAT_BGRX1010102: 55995718399fSFrançois Tigeot case DRM_FORMAT_ARGB2101010: 56005718399fSFrançois Tigeot case DRM_FORMAT_ABGR2101010: 56015718399fSFrançois Tigeot case DRM_FORMAT_RGBA1010102: 56025718399fSFrançois Tigeot case DRM_FORMAT_BGRA1010102: 56035718399fSFrançois Tigeot *depth = 30; 56045718399fSFrançois Tigeot *bpp = 32; 56055718399fSFrançois Tigeot break; 56065718399fSFrançois Tigeot case DRM_FORMAT_ARGB8888: 56075718399fSFrançois Tigeot case DRM_FORMAT_ABGR8888: 56085718399fSFrançois Tigeot case DRM_FORMAT_RGBA8888: 56095718399fSFrançois Tigeot case DRM_FORMAT_BGRA8888: 56105718399fSFrançois Tigeot *depth = 32; 56115718399fSFrançois Tigeot *bpp = 32; 56125718399fSFrançois Tigeot break; 56135718399fSFrançois Tigeot default: 56149edbd4a0SFrançois Tigeot DRM_DEBUG_KMS("unsupported pixel format %s\n", 56159edbd4a0SFrançois Tigeot drm_get_format_name(format)); 56165718399fSFrançois Tigeot *depth = 0; 56175718399fSFrançois Tigeot *bpp = 0; 56185718399fSFrançois Tigeot break; 56195718399fSFrançois Tigeot } 56205718399fSFrançois Tigeot } 5621b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_fb_get_bpp_depth); 5622b5162e19SFrançois Tigeot 5623b5162e19SFrançois Tigeot /** 5624b5162e19SFrançois Tigeot * drm_format_num_planes - get the number of planes for format 5625b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 5626b5162e19SFrançois Tigeot * 5627ba55f2f5SFrançois Tigeot * Returns: 5628b5162e19SFrançois Tigeot * The number of planes used by the specified pixel format. 5629b5162e19SFrançois Tigeot */ 5630b5162e19SFrançois Tigeot int drm_format_num_planes(uint32_t format) 5631b5162e19SFrançois Tigeot { 5632b5162e19SFrançois Tigeot switch (format) { 5633b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 5634b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 5635b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 5636b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 5637b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 5638b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 5639b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 5640b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 5641b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 5642b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 5643b5162e19SFrançois Tigeot return 3; 5644b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 5645b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 5646b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 5647b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 5648b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 5649b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 5650b5162e19SFrançois Tigeot return 2; 5651b5162e19SFrançois Tigeot default: 5652b5162e19SFrançois Tigeot return 1; 5653b5162e19SFrançois Tigeot } 5654b5162e19SFrançois Tigeot } 5655b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_num_planes); 5656b5162e19SFrançois Tigeot 5657b5162e19SFrançois Tigeot /** 5658b5162e19SFrançois Tigeot * drm_format_plane_cpp - determine the bytes per pixel value 5659b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 5660b5162e19SFrançois Tigeot * @plane: plane index 5661b5162e19SFrançois Tigeot * 5662ba55f2f5SFrançois Tigeot * Returns: 5663b5162e19SFrançois Tigeot * The bytes per pixel value for the specified plane. 5664b5162e19SFrançois Tigeot */ 5665b5162e19SFrançois Tigeot int drm_format_plane_cpp(uint32_t format, int plane) 5666b5162e19SFrançois Tigeot { 5667b5162e19SFrançois Tigeot unsigned int depth; 5668b5162e19SFrançois Tigeot int bpp; 5669b5162e19SFrançois Tigeot 5670b5162e19SFrançois Tigeot if (plane >= drm_format_num_planes(format)) 5671b5162e19SFrançois Tigeot return 0; 5672b5162e19SFrançois Tigeot 5673b5162e19SFrançois Tigeot switch (format) { 5674b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 5675b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 5676b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 5677b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 5678b5162e19SFrançois Tigeot return 2; 5679b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 5680b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 5681b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 5682b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 5683b5162e19SFrançois Tigeot case DRM_FORMAT_NV24: 5684b5162e19SFrançois Tigeot case DRM_FORMAT_NV42: 5685b5162e19SFrançois Tigeot return plane ? 2 : 1; 5686b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 5687b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 5688b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 5689b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 5690b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 5691b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 5692b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 5693b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 5694b5162e19SFrançois Tigeot case DRM_FORMAT_YUV444: 5695b5162e19SFrançois Tigeot case DRM_FORMAT_YVU444: 5696b5162e19SFrançois Tigeot return 1; 5697b5162e19SFrançois Tigeot default: 5698b5162e19SFrançois Tigeot drm_fb_get_bpp_depth(format, &depth, &bpp); 5699b5162e19SFrançois Tigeot return bpp >> 3; 5700b5162e19SFrançois Tigeot } 5701b5162e19SFrançois Tigeot } 5702b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_cpp); 5703b5162e19SFrançois Tigeot 5704b5162e19SFrançois Tigeot /** 5705b5162e19SFrançois Tigeot * drm_format_horz_chroma_subsampling - get the horizontal chroma subsampling factor 5706b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 5707b5162e19SFrançois Tigeot * 5708ba55f2f5SFrançois Tigeot * Returns: 5709b5162e19SFrançois Tigeot * The horizontal chroma subsampling factor for the 5710b5162e19SFrançois Tigeot * specified pixel format. 5711b5162e19SFrançois Tigeot */ 5712b5162e19SFrançois Tigeot int drm_format_horz_chroma_subsampling(uint32_t format) 5713b5162e19SFrançois Tigeot { 5714b5162e19SFrançois Tigeot switch (format) { 5715b5162e19SFrançois Tigeot case DRM_FORMAT_YUV411: 5716b5162e19SFrançois Tigeot case DRM_FORMAT_YVU411: 5717b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 5718b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 5719b5162e19SFrançois Tigeot return 4; 5720b5162e19SFrançois Tigeot case DRM_FORMAT_YUYV: 5721b5162e19SFrançois Tigeot case DRM_FORMAT_YVYU: 5722b5162e19SFrançois Tigeot case DRM_FORMAT_UYVY: 5723b5162e19SFrançois Tigeot case DRM_FORMAT_VYUY: 5724b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 5725b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 5726b5162e19SFrançois Tigeot case DRM_FORMAT_NV16: 5727b5162e19SFrançois Tigeot case DRM_FORMAT_NV61: 5728b5162e19SFrançois Tigeot case DRM_FORMAT_YUV422: 5729b5162e19SFrançois Tigeot case DRM_FORMAT_YVU422: 5730b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 5731b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 5732b5162e19SFrançois Tigeot return 2; 5733b5162e19SFrançois Tigeot default: 5734b5162e19SFrançois Tigeot return 1; 5735b5162e19SFrançois Tigeot } 5736b5162e19SFrançois Tigeot } 5737b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_horz_chroma_subsampling); 5738b5162e19SFrançois Tigeot 5739b5162e19SFrançois Tigeot /** 5740b5162e19SFrançois Tigeot * drm_format_vert_chroma_subsampling - get the vertical chroma subsampling factor 5741b5162e19SFrançois Tigeot * @format: pixel format (DRM_FORMAT_*) 5742b5162e19SFrançois Tigeot * 5743ba55f2f5SFrançois Tigeot * Returns: 5744b5162e19SFrançois Tigeot * The vertical chroma subsampling factor for the 5745b5162e19SFrançois Tigeot * specified pixel format. 5746b5162e19SFrançois Tigeot */ 5747b5162e19SFrançois Tigeot int drm_format_vert_chroma_subsampling(uint32_t format) 5748b5162e19SFrançois Tigeot { 5749b5162e19SFrançois Tigeot switch (format) { 5750b5162e19SFrançois Tigeot case DRM_FORMAT_YUV410: 5751b5162e19SFrançois Tigeot case DRM_FORMAT_YVU410: 5752b5162e19SFrançois Tigeot return 4; 5753b5162e19SFrançois Tigeot case DRM_FORMAT_YUV420: 5754b5162e19SFrançois Tigeot case DRM_FORMAT_YVU420: 5755b5162e19SFrançois Tigeot case DRM_FORMAT_NV12: 5756b5162e19SFrançois Tigeot case DRM_FORMAT_NV21: 5757b5162e19SFrançois Tigeot return 2; 5758b5162e19SFrançois Tigeot default: 5759b5162e19SFrançois Tigeot return 1; 5760b5162e19SFrançois Tigeot } 5761b5162e19SFrançois Tigeot } 5762b5162e19SFrançois Tigeot EXPORT_SYMBOL(drm_format_vert_chroma_subsampling); 57634dbb207bSFrançois Tigeot 57644dbb207bSFrançois Tigeot /** 5765c0e85e96SFrançois Tigeot * drm_format_plane_width - width of the plane given the first plane 5766c0e85e96SFrançois Tigeot * @width: width of the first plane 5767c0e85e96SFrançois Tigeot * @format: pixel format 5768c0e85e96SFrançois Tigeot * @plane: plane index 5769c0e85e96SFrançois Tigeot * 5770c0e85e96SFrançois Tigeot * Returns: 5771c0e85e96SFrançois Tigeot * The width of @plane, given that the width of the first plane is @width. 5772c0e85e96SFrançois Tigeot */ 5773c0e85e96SFrançois Tigeot int drm_format_plane_width(int width, uint32_t format, int plane) 5774c0e85e96SFrançois Tigeot { 5775c0e85e96SFrançois Tigeot if (plane >= drm_format_num_planes(format)) 5776c0e85e96SFrançois Tigeot return 0; 5777c0e85e96SFrançois Tigeot 5778c0e85e96SFrançois Tigeot if (plane == 0) 5779c0e85e96SFrançois Tigeot return width; 5780c0e85e96SFrançois Tigeot 5781c0e85e96SFrançois Tigeot return width / drm_format_horz_chroma_subsampling(format); 5782c0e85e96SFrançois Tigeot } 5783c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_width); 5784c0e85e96SFrançois Tigeot 5785c0e85e96SFrançois Tigeot /** 5786c0e85e96SFrançois Tigeot * drm_format_plane_height - height of the plane given the first plane 5787c0e85e96SFrançois Tigeot * @height: height of the first plane 5788c0e85e96SFrançois Tigeot * @format: pixel format 5789c0e85e96SFrançois Tigeot * @plane: plane index 5790c0e85e96SFrançois Tigeot * 5791c0e85e96SFrançois Tigeot * Returns: 5792c0e85e96SFrançois Tigeot * The height of @plane, given that the height of the first plane is @height. 5793c0e85e96SFrançois Tigeot */ 5794c0e85e96SFrançois Tigeot int drm_format_plane_height(int height, uint32_t format, int plane) 5795c0e85e96SFrançois Tigeot { 5796c0e85e96SFrançois Tigeot if (plane >= drm_format_num_planes(format)) 5797c0e85e96SFrançois Tigeot return 0; 5798c0e85e96SFrançois Tigeot 5799c0e85e96SFrançois Tigeot if (plane == 0) 5800c0e85e96SFrançois Tigeot return height; 5801c0e85e96SFrançois Tigeot 5802c0e85e96SFrançois Tigeot return height / drm_format_vert_chroma_subsampling(format); 5803c0e85e96SFrançois Tigeot } 5804c0e85e96SFrançois Tigeot EXPORT_SYMBOL(drm_format_plane_height); 5805c0e85e96SFrançois Tigeot 5806c0e85e96SFrançois Tigeot /** 58072c9916cdSFrançois Tigeot * drm_rotation_simplify() - Try to simplify the rotation 58082c9916cdSFrançois Tigeot * @rotation: Rotation to be simplified 58092c9916cdSFrançois Tigeot * @supported_rotations: Supported rotations 58102c9916cdSFrançois Tigeot * 58112c9916cdSFrançois Tigeot * Attempt to simplify the rotation to a form that is supported. 58122c9916cdSFrançois Tigeot * Eg. if the hardware supports everything except DRM_REFLECT_X 58132c9916cdSFrançois Tigeot * one could call this function like this: 58142c9916cdSFrançois Tigeot * 58152c9916cdSFrançois Tigeot * drm_rotation_simplify(rotation, BIT(DRM_ROTATE_0) | 58162c9916cdSFrançois Tigeot * BIT(DRM_ROTATE_90) | BIT(DRM_ROTATE_180) | 58172c9916cdSFrançois Tigeot * BIT(DRM_ROTATE_270) | BIT(DRM_REFLECT_Y)); 58182c9916cdSFrançois Tigeot * 58192c9916cdSFrançois Tigeot * to eliminate the DRM_ROTATE_X flag. Depending on what kind of 58202c9916cdSFrançois Tigeot * transforms the hardware supports, this function may not 58212c9916cdSFrançois Tigeot * be able to produce a supported transform, so the caller should 58222c9916cdSFrançois Tigeot * check the result afterwards. 58232c9916cdSFrançois Tigeot */ 58242c9916cdSFrançois Tigeot unsigned int drm_rotation_simplify(unsigned int rotation, 58252c9916cdSFrançois Tigeot unsigned int supported_rotations) 58262c9916cdSFrançois Tigeot { 58272c9916cdSFrançois Tigeot if (rotation & ~supported_rotations) { 58282c9916cdSFrançois Tigeot rotation ^= BIT(DRM_REFLECT_X) | BIT(DRM_REFLECT_Y); 5829352ff8bdSFrançois Tigeot rotation = (rotation & DRM_REFLECT_MASK) | 5830352ff8bdSFrançois Tigeot BIT((ffs(rotation & DRM_ROTATE_MASK) + 1) % 4); 58312c9916cdSFrançois Tigeot } 58322c9916cdSFrançois Tigeot 58332c9916cdSFrançois Tigeot return rotation; 58342c9916cdSFrançois Tigeot } 58352c9916cdSFrançois Tigeot EXPORT_SYMBOL(drm_rotation_simplify); 58362c9916cdSFrançois Tigeot 58372c9916cdSFrançois Tigeot /** 58384dbb207bSFrançois Tigeot * drm_mode_config_init - initialize DRM mode_configuration structure 58394dbb207bSFrançois Tigeot * @dev: DRM device 58404dbb207bSFrançois Tigeot * 58414dbb207bSFrançois Tigeot * Initialize @dev's mode_config structure, used for tracking the graphics 58424dbb207bSFrançois Tigeot * configuration of @dev. 58434dbb207bSFrançois Tigeot * 58444dbb207bSFrançois Tigeot * Since this initializes the modeset locks, no locking is possible. Which is no 58454dbb207bSFrançois Tigeot * problem, since this should happen single threaded at init time. It is the 58464dbb207bSFrançois Tigeot * driver's problem to ensure this guarantee. 58474dbb207bSFrançois Tigeot * 58484dbb207bSFrançois Tigeot */ 58494dbb207bSFrançois Tigeot void drm_mode_config_init(struct drm_device *dev) 58504dbb207bSFrançois Tigeot { 58514dbb207bSFrançois Tigeot lockinit(&dev->mode_config.mutex, "drmmcm", 0, LK_CANRECURSE); 5852ba55f2f5SFrançois Tigeot drm_modeset_lock_init(&dev->mode_config.connection_mutex); 58534dbb207bSFrançois Tigeot lockinit(&dev->mode_config.idr_mutex, "mcfgidr", 0, LK_CANRECURSE); 58544dbb207bSFrançois Tigeot lockinit(&dev->mode_config.fb_lock, "drmfbl", 0, LK_CANRECURSE); 585519c468b4SFrançois Tigeot lockinit(&dev->mode_config.blob_lock, "drmcbl", 0, LK_CANRECURSE); 58564dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.fb_list); 58574dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.crtc_list); 58584dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.connector_list); 58594dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.encoder_list); 58604dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_list); 58614dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.property_blob_list); 58624dbb207bSFrançois Tigeot INIT_LIST_HEAD(&dev->mode_config.plane_list); 58634dbb207bSFrançois Tigeot idr_init(&dev->mode_config.crtc_idr); 58642c9916cdSFrançois Tigeot idr_init(&dev->mode_config.tile_idr); 5865917ec290SFrançois Tigeot ida_init(&dev->mode_config.connector_ida); 58664dbb207bSFrançois Tigeot 58674dbb207bSFrançois Tigeot drm_modeset_lock_all(dev); 58682c9916cdSFrançois Tigeot drm_mode_create_standard_properties(dev); 58694dbb207bSFrançois Tigeot drm_modeset_unlock_all(dev); 58704dbb207bSFrançois Tigeot 58714dbb207bSFrançois Tigeot /* Just to be sure */ 58724dbb207bSFrançois Tigeot dev->mode_config.num_fb = 0; 58734dbb207bSFrançois Tigeot dev->mode_config.num_connector = 0; 58744dbb207bSFrançois Tigeot dev->mode_config.num_crtc = 0; 58754dbb207bSFrançois Tigeot dev->mode_config.num_encoder = 0; 5876ba55f2f5SFrançois Tigeot dev->mode_config.num_overlay_plane = 0; 5877ba55f2f5SFrançois Tigeot dev->mode_config.num_total_plane = 0; 58784dbb207bSFrançois Tigeot } 58794dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_init); 58804dbb207bSFrançois Tigeot 58814dbb207bSFrançois Tigeot /** 58824dbb207bSFrançois Tigeot * drm_mode_config_cleanup - free up DRM mode_config info 58834dbb207bSFrançois Tigeot * @dev: DRM device 58844dbb207bSFrançois Tigeot * 58854dbb207bSFrançois Tigeot * Free up all the connectors and CRTCs associated with this DRM device, then 58864dbb207bSFrançois Tigeot * free up the framebuffers and associated buffer objects. 58874dbb207bSFrançois Tigeot * 58884dbb207bSFrançois Tigeot * Note that since this /should/ happen single-threaded at driver/device 58894dbb207bSFrançois Tigeot * teardown time, no locking is required. It's the driver's job to ensure that 58904dbb207bSFrançois Tigeot * this guarantee actually holds true. 58914dbb207bSFrançois Tigeot * 58924dbb207bSFrançois Tigeot * FIXME: cleanup any dangling user buffer objects too 58934dbb207bSFrançois Tigeot */ 58944dbb207bSFrançois Tigeot void drm_mode_config_cleanup(struct drm_device *dev) 58954dbb207bSFrançois Tigeot { 58964dbb207bSFrançois Tigeot struct drm_connector *connector, *ot; 58974dbb207bSFrançois Tigeot struct drm_crtc *crtc, *ct; 58984dbb207bSFrançois Tigeot struct drm_encoder *encoder, *enct; 58994dbb207bSFrançois Tigeot struct drm_framebuffer *fb, *fbt; 59004dbb207bSFrançois Tigeot struct drm_property *property, *pt; 59014dbb207bSFrançois Tigeot struct drm_property_blob *blob, *bt; 59024dbb207bSFrançois Tigeot struct drm_plane *plane, *plt; 59034dbb207bSFrançois Tigeot 59044dbb207bSFrançois Tigeot list_for_each_entry_safe(encoder, enct, &dev->mode_config.encoder_list, 59054dbb207bSFrançois Tigeot head) { 59064dbb207bSFrançois Tigeot encoder->funcs->destroy(encoder); 59074dbb207bSFrançois Tigeot } 59084dbb207bSFrançois Tigeot 59094dbb207bSFrançois Tigeot list_for_each_entry_safe(connector, ot, 59104dbb207bSFrançois Tigeot &dev->mode_config.connector_list, head) { 59114dbb207bSFrançois Tigeot connector->funcs->destroy(connector); 59124dbb207bSFrançois Tigeot } 59134dbb207bSFrançois Tigeot 59144dbb207bSFrançois Tigeot list_for_each_entry_safe(property, pt, &dev->mode_config.property_list, 59154dbb207bSFrançois Tigeot head) { 59164dbb207bSFrançois Tigeot drm_property_destroy(dev, property); 59174dbb207bSFrançois Tigeot } 59184dbb207bSFrançois Tigeot 59198621f407SFrançois Tigeot list_for_each_entry_safe(plane, plt, &dev->mode_config.plane_list, 59208621f407SFrançois Tigeot head) { 59218621f407SFrançois Tigeot plane->funcs->destroy(plane); 59228621f407SFrançois Tigeot } 59238621f407SFrançois Tigeot 59248621f407SFrançois Tigeot list_for_each_entry_safe(crtc, ct, &dev->mode_config.crtc_list, head) { 59258621f407SFrançois Tigeot crtc->funcs->destroy(crtc); 59268621f407SFrançois Tigeot } 59278621f407SFrançois Tigeot 59284dbb207bSFrançois Tigeot list_for_each_entry_safe(blob, bt, &dev->mode_config.property_blob_list, 592919c468b4SFrançois Tigeot head_global) { 593019c468b4SFrançois Tigeot drm_property_unreference_blob(blob); 59314dbb207bSFrançois Tigeot } 59324dbb207bSFrançois Tigeot 59334dbb207bSFrançois Tigeot /* 59344dbb207bSFrançois Tigeot * Single-threaded teardown context, so it's not required to grab the 59354dbb207bSFrançois Tigeot * fb_lock to protect against concurrent fb_list access. Contrary, it 59364dbb207bSFrançois Tigeot * would actually deadlock with the drm_framebuffer_cleanup function. 59374dbb207bSFrançois Tigeot * 59384dbb207bSFrançois Tigeot * Also, if there are any framebuffers left, that's a driver leak now, 59394dbb207bSFrançois Tigeot * so politely WARN about this. 59404dbb207bSFrançois Tigeot */ 59414dbb207bSFrançois Tigeot WARN_ON(!list_empty(&dev->mode_config.fb_list)); 59424dbb207bSFrançois Tigeot list_for_each_entry_safe(fb, fbt, &dev->mode_config.fb_list, head) { 59438621f407SFrançois Tigeot drm_framebuffer_free(&fb->base.refcount); 59444dbb207bSFrançois Tigeot } 59454dbb207bSFrançois Tigeot 5946917ec290SFrançois Tigeot ida_destroy(&dev->mode_config.connector_ida); 59472c9916cdSFrançois Tigeot idr_destroy(&dev->mode_config.tile_idr); 59484dbb207bSFrançois Tigeot idr_destroy(&dev->mode_config.crtc_idr); 5949ba55f2f5SFrançois Tigeot drm_modeset_lock_fini(&dev->mode_config.connection_mutex); 59504dbb207bSFrançois Tigeot } 59514dbb207bSFrançois Tigeot EXPORT_SYMBOL(drm_mode_config_cleanup); 59521b13d190SFrançois Tigeot 59531b13d190SFrançois Tigeot struct drm_property *drm_mode_create_rotation_property(struct drm_device *dev, 59541b13d190SFrançois Tigeot unsigned int supported_rotations) 59551b13d190SFrançois Tigeot { 59561b13d190SFrançois Tigeot static const struct drm_prop_enum_list props[] = { 59571b13d190SFrançois Tigeot { DRM_ROTATE_0, "rotate-0" }, 59581b13d190SFrançois Tigeot { DRM_ROTATE_90, "rotate-90" }, 59591b13d190SFrançois Tigeot { DRM_ROTATE_180, "rotate-180" }, 59601b13d190SFrançois Tigeot { DRM_ROTATE_270, "rotate-270" }, 59611b13d190SFrançois Tigeot { DRM_REFLECT_X, "reflect-x" }, 59621b13d190SFrançois Tigeot { DRM_REFLECT_Y, "reflect-y" }, 59631b13d190SFrançois Tigeot }; 59641b13d190SFrançois Tigeot 59651b13d190SFrançois Tigeot return drm_property_create_bitmask(dev, 0, "rotation", 59661b13d190SFrançois Tigeot props, ARRAY_SIZE(props), 59671b13d190SFrançois Tigeot supported_rotations); 59681b13d190SFrançois Tigeot } 59691b13d190SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_rotation_property); 59702c9916cdSFrançois Tigeot 59712c9916cdSFrançois Tigeot /** 59722c9916cdSFrançois Tigeot * DOC: Tile group 59732c9916cdSFrançois Tigeot * 59742c9916cdSFrançois Tigeot * Tile groups are used to represent tiled monitors with a unique 59752c9916cdSFrançois Tigeot * integer identifier. Tiled monitors using DisplayID v1.3 have 59762c9916cdSFrançois Tigeot * a unique 8-byte handle, we store this in a tile group, so we 59772c9916cdSFrançois Tigeot * have a common identifier for all tiles in a monitor group. 59782c9916cdSFrançois Tigeot */ 59792c9916cdSFrançois Tigeot static void drm_tile_group_free(struct kref *kref) 59802c9916cdSFrançois Tigeot { 59812c9916cdSFrançois Tigeot struct drm_tile_group *tg = container_of(kref, struct drm_tile_group, refcount); 59822c9916cdSFrançois Tigeot struct drm_device *dev = tg->dev; 59832c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 59842c9916cdSFrançois Tigeot idr_remove(&dev->mode_config.tile_idr, tg->id); 59852c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 59862c9916cdSFrançois Tigeot kfree(tg); 59872c9916cdSFrançois Tigeot } 59882c9916cdSFrançois Tigeot 59892c9916cdSFrançois Tigeot /** 59902c9916cdSFrançois Tigeot * drm_mode_put_tile_group - drop a reference to a tile group. 59912c9916cdSFrançois Tigeot * @dev: DRM device 59922c9916cdSFrançois Tigeot * @tg: tile group to drop reference to. 59932c9916cdSFrançois Tigeot * 59942c9916cdSFrançois Tigeot * drop reference to tile group and free if 0. 59952c9916cdSFrançois Tigeot */ 59962c9916cdSFrançois Tigeot void drm_mode_put_tile_group(struct drm_device *dev, 59972c9916cdSFrançois Tigeot struct drm_tile_group *tg) 59982c9916cdSFrançois Tigeot { 59992c9916cdSFrançois Tigeot kref_put(&tg->refcount, drm_tile_group_free); 60002c9916cdSFrançois Tigeot } 60012c9916cdSFrançois Tigeot 60022c9916cdSFrançois Tigeot /** 60032c9916cdSFrançois Tigeot * drm_mode_get_tile_group - get a reference to an existing tile group 60042c9916cdSFrançois Tigeot * @dev: DRM device 60052c9916cdSFrançois Tigeot * @topology: 8-bytes unique per monitor. 60062c9916cdSFrançois Tigeot * 60072c9916cdSFrançois Tigeot * Use the unique bytes to get a reference to an existing tile group. 60082c9916cdSFrançois Tigeot * 60092c9916cdSFrançois Tigeot * RETURNS: 60102c9916cdSFrançois Tigeot * tile group or NULL if not found. 60112c9916cdSFrançois Tigeot */ 60122c9916cdSFrançois Tigeot struct drm_tile_group *drm_mode_get_tile_group(struct drm_device *dev, 60132c9916cdSFrançois Tigeot char topology[8]) 60142c9916cdSFrançois Tigeot { 60152c9916cdSFrançois Tigeot #if 0 60162c9916cdSFrançois Tigeot struct drm_tile_group *tg; 60172c9916cdSFrançois Tigeot int id; 60182c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 60192c9916cdSFrançois Tigeot idr_for_each_entry(&dev->mode_config.tile_idr, tg, id) { 60202c9916cdSFrançois Tigeot if (!memcmp(tg->group_data, topology, 8)) { 60212c9916cdSFrançois Tigeot if (!kref_get_unless_zero(&tg->refcount)) 60222c9916cdSFrançois Tigeot tg = NULL; 60232c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 60242c9916cdSFrançois Tigeot return tg; 60252c9916cdSFrançois Tigeot } 60262c9916cdSFrançois Tigeot } 60272c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 60282c9916cdSFrançois Tigeot #endif 60292c9916cdSFrançois Tigeot return NULL; 60302c9916cdSFrançois Tigeot } 6031477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_get_tile_group); 60322c9916cdSFrançois Tigeot 60332c9916cdSFrançois Tigeot /** 60342c9916cdSFrançois Tigeot * drm_mode_create_tile_group - create a tile group from a displayid description 60352c9916cdSFrançois Tigeot * @dev: DRM device 60362c9916cdSFrançois Tigeot * @topology: 8-bytes unique per monitor. 60372c9916cdSFrançois Tigeot * 60382c9916cdSFrançois Tigeot * Create a tile group for the unique monitor, and get a unique 60392c9916cdSFrançois Tigeot * identifier for the tile group. 60402c9916cdSFrançois Tigeot * 60412c9916cdSFrançois Tigeot * RETURNS: 60422c9916cdSFrançois Tigeot * new tile group or error. 60432c9916cdSFrançois Tigeot */ 60442c9916cdSFrançois Tigeot struct drm_tile_group *drm_mode_create_tile_group(struct drm_device *dev, 60452c9916cdSFrançois Tigeot char topology[8]) 60462c9916cdSFrançois Tigeot { 60472c9916cdSFrançois Tigeot struct drm_tile_group *tg; 60482c9916cdSFrançois Tigeot int ret; 60492c9916cdSFrançois Tigeot 60502c9916cdSFrançois Tigeot tg = kzalloc(sizeof(*tg), GFP_KERNEL); 60512c9916cdSFrançois Tigeot if (!tg) 60522c9916cdSFrançois Tigeot return ERR_PTR(-ENOMEM); 60532c9916cdSFrançois Tigeot 60542c9916cdSFrançois Tigeot kref_init(&tg->refcount); 60552c9916cdSFrançois Tigeot memcpy(tg->group_data, topology, 8); 60562c9916cdSFrançois Tigeot tg->dev = dev; 60572c9916cdSFrançois Tigeot 60582c9916cdSFrançois Tigeot mutex_lock(&dev->mode_config.idr_mutex); 60592c9916cdSFrançois Tigeot ret = idr_alloc(&dev->mode_config.tile_idr, tg, 1, 0, GFP_KERNEL); 60602c9916cdSFrançois Tigeot if (ret >= 0) { 60612c9916cdSFrançois Tigeot tg->id = ret; 60622c9916cdSFrançois Tigeot } else { 60632c9916cdSFrançois Tigeot kfree(tg); 60642c9916cdSFrançois Tigeot tg = ERR_PTR(ret); 60652c9916cdSFrançois Tigeot } 60662c9916cdSFrançois Tigeot 60672c9916cdSFrançois Tigeot mutex_unlock(&dev->mode_config.idr_mutex); 60682c9916cdSFrançois Tigeot return tg; 60692c9916cdSFrançois Tigeot } 6070477eb7f9SFrançois Tigeot EXPORT_SYMBOL(drm_mode_create_tile_group); 6071